retry
Runs a flaky effect again until it succeeds — up to attempts times, with optional backoff between failures.
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.
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