withStreamRetry<T> function
Execute an async operation with exponential backoff and jitter.
This is a streaming-aware retry wrapper that complements the existing
withRetry in retry.dart. It operates on raw futures rather than
ApiError types, making it suitable for use outside the ApiProvider
abstraction.
Implementation
Future<T> withStreamRetry<T>(
Future<T> Function() fn, {
int maxRetries = 3,
Duration initialDelay = const Duration(seconds: 1),
Duration maxDelay = const Duration(seconds: 30),
bool Function(Object error)? shouldRetry,
}) async {
final random = Random();
var attempt = 0;
while (true) {
try {
return await fn();
} catch (e) {
attempt++;
if (attempt >= maxRetries) rethrow;
// Check custom retry predicate.
if (shouldRetry != null && !shouldRetry(e)) rethrow;
// Default: retry on ApiError if retryable, otherwise don't.
if (shouldRetry == null) {
if (e is ApiError && !e.isRetryable) rethrow;
}
// Calculate backoff with jitter.
final baseMs = initialDelay.inMilliseconds * pow(2, attempt - 1);
final jitter = random.nextDouble() * 0.25 * baseMs;
final delayMs = (baseMs + jitter).toInt().clamp(
initialDelay.inMilliseconds,
maxDelay.inMilliseconds,
);
await Future<void>.delayed(Duration(milliseconds: delayMs));
}
}
}