retry

불안정한 효과를 성공할 때까지 다시 실행합니다 — 최대 attempts번, 실패 사이에 선택적 백오프를 두고서.

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)

강의

실제 파이프라인은 실제 서비스를 호출하고, 실제 서비스는 가끔 실패합니다. 손으로 짠 답은 try/catch와 카운터, Future.delayed를 갖춘 for 루프 — 프로젝트마다 복사되면서 매번 미묘하게 달라지는 코드입니다. retry(attempts, f)는 그 루프를 한 번으로 정리한 것입니다. f를 실행하고, 에러가 나면 다시 실행하되 총 attempts번까지만 실행합니다. 예산이 다 떨어지면 마지막 에러가 원래 스택 트레이스와 함께 다시 던져집니다. delay 훅은 실패 횟수(1, 2, …)를 받으므로 백오프는 한 줄이면 됩니다: delay: (failed) => Duration(seconds: failed).

mapRetry(attempts, f)는 같은 아이디어를 원소 단위로 적용한 것입니다. 모든 호출이 각자의 재시도 예산을 갖는 map입니다. 병렬 안전한 mapAsync 위에 만들어져 있어서 concurrent(n) 아래에서는 진행 중인 각 원소가 독립적으로 재시도합니다 — 느리고 불안정한 항목 하나가 다시 실행되는 동안 이웃들은 그대로 지나가고, 순서는 여전히 보존됩니다. 파이프라인 전체를 재시도하려면 대신 종단을 감싸세요: retry(3, () => fxAsync(…).toList()) — 부분 결과는 버려지고 파이프라인은 새 이터레이터에서 처음부터 다시 실행됩니다.

Dart 고유의 추가 기능입니다(FxTS에는 대응물이 없습니다). Rx의 retry/retryWhen을 따르되, "재구독"이 "이터러블을 다시 만든다"를 뜻하는 pull 모델에 맞춰 다시 설계했습니다. 재시도가 소진된 뒤의 타입이 있는 실패 처리는 결과를 eitherCatching에 넘기세요.

데모 1 · 불안정한 fetch, 백오프와 함께

데모 2 · concurrent 아래의 mapRetry

직접 해 보기

연습: 불안정한 행이 있어도 임포트가 살아남게 만들어 보세요.

관련 항목: timeout — 각 pull이 걸릴 수 있는 시간에 한도 두기 · concurrent — 진행 중인 원소마다 재시도가 독립적으로 유지됨 · 타입 있는 에러 — 실패가 예외가 아니라 값이어야 할 때