retry

Runs a flaky effect again until it succeeds — up to attempts times, with optional backoff between failures.

Future<T> retry<T>(int attempts, FutureOr<T> Function() f, {Duration Function(int failed)? delay}) FxAsyncIterable<R> mapRetryAsync<A, R>(int attempts, FutureOr<R> Function(A a) f, FxAsyncIterable<A> iterable, {Duration Function(int failed)? delay}) FxAsync<R> Fx<T>.mapRetry<R>(int attempts, FutureOr<R> Function(T a) f, {Duration Function(int failed)? delay}) // chain (sync → async) FxAsync<R> FxAsync<T>.mapRetry<R>(int attempts, FutureOr<R> Function(T a) f, {Duration Function(int failed)? delay}) // chain (async)

Lecture

Real pipelines call real services, and real services flake. The hand-rolled answer is a for loop with a try/catch, a counter, and a Future.delayed — copied into every project, subtly different each time. retry(attempts, f) is that loop, once: run f, and on error run it again, up to attempts total runs; when the budget is spent the last error rethrows with its original stack trace. The delay hook receives the failure count (1, 2, …), so backoff is one line: delay: (failed) => Duration(seconds: failed).

mapRetry(attempts, f) is the same idea per element: a map whose every call gets its own retry budget. It is built on the parallel-safe mapAsync, so under concurrent(n) each in-flight element retries independently — one slow, flaky item re-runs while its neighbors sail through, and order is still preserved. To retry a whole pipeline instead, wrap its terminal: retry(3, () => fxAsync(…).toList()) — the partial result is discarded and the pipeline re-runs from a fresh iterator.

fxdart extension (no FxTS counterpart), after Rx's retry/retryWhen — re-designed for the pull model, where "resubscribe" means "build the iterable again". For typed failure handling after the retries run out, hand the result to eitherCatching.

Demo 1 · A flaky fetch, with backoff

Demo 2 · mapRetry under concurrent

Try it yourself

Exercise: make the import survive its flaky rows.

Related: timeout — bound how long each pull may take · concurrent — retries stay independent per in-flight element · typed errors — when the failure should be a value, not an exception