retry

Ejecuta de nuevo un efecto inestable hasta que tenga éxito — hasta attempts veces, con backoff opcional entre fallos.

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)

Lección

Los pipelines reales llaman a servicios reales, y los servicios reales fallan de vez en cuando. La respuesta hecha a mano es un bucle for con un try/catch, un contador y un Future.delayed — copiada en cada proyecto, sutilmente distinta cada vez. retry(attempts, f) es ese bucle, una sola vez: ejecuta f, y ante un error vuelve a ejecutarlo, hasta attempts ejecuciones en total; agotado el presupuesto, el último error se relanza con su stack trace original. El hook delay recibe el número de fallos (1, 2, …), así que el backoff es una línea: delay: (failed) => Duration(seconds: failed).

mapRetry(attempts, f) es la misma idea por elemento: un map en el que cada llamada tiene su propio presupuesto de reintentos. Está construido sobre el mapAsync seguro en paralelo, así que bajo concurrent(n) cada elemento en vuelo reintenta de forma independiente — un elemento lento e inestable se reejecuta mientras sus vecinos avanzan sin problema, y el orden se sigue preservando. Para reintentar un pipeline entero, envuelve su terminal: retry(3, () => fxAsync(…).toList()) — el resultado parcial se descarta y el pipeline se reejecuta desde un iterador nuevo.

Extensión de fxdart (sin contraparte en FxTS), inspirada en retry/retryWhen de Rx — rediseñada para el modelo pull, donde «resuscribirse» significa «construir el iterable otra vez». Para un manejo tipado del fallo cuando los reintentos se agotan, pásale el resultado a eitherCatching.

Demo 1 · Un fetch inestable, con backoff

Demo 2 · mapRetry bajo concurrent

Pruébalo tú

Ejercicio: haz que la importación sobreviva a sus filas inestables.

Relacionado: timeout — acota cuánto puede tardar cada pull · concurrent — los reintentos son independientes por elemento en vuelo · errores tipados — cuando el fallo debe ser un valor, no una excepción