Skip to content

retry ​

Problem ​

A network call or external API is flaky and fails occasionally. You need automatic retries with configurable delay and the ability to cancel early or skip certain error types.

Solution ​

Use retry(fn, options) to re-run an async function up to times attempts. The callback receives an AbortSignal that fires when a per-attempt timeout expires or the external signal is aborted.

ts
import { retry, backoff } from '@vielzeug/arsenal';

const data = await retry((signal) => fetch('/api/health', { signal }).then((r) => r.json()), {
  times: 4,
  timeout: 5_000,
  delay: (failureIndex) => backoff(failureIndex), // 1s, 2s, 4s, 8s
  shouldRetry: (err, failureIndex) => {
    // failureIndex is 0-based: 0 = first failure
    // NOT called on the final exhausting attempt
    return failureIndex < 3 && !(err instanceof TypeError);
  },
});

Basic usage ​

ts
import { retry } from '@vielzeug/arsenal';

const result = await retry(() => fetch('/api').then((r) => r.json()), { times: 3, delay: 250 });

With external cancellation ​

ts
import { retry } from '@vielzeug/arsenal';

const controller = new AbortController();

const result = await retry((signal) => fetch('/api/data', { signal }).then((r) => r.json()), {
  times: 3,
  signal: controller.signal,
});

controller.abort(); // cancels retries mid-flight

Pitfalls ​

  • shouldRetry is not called on the final (exhausting) attempt — it only guards intermediate retries.
  • failureIndex is 0-based: index 0 is the first failure, index 1 is the second, and so on.
  • When a timeout is set, the signal passed to fn fires after timeout ms — pass it to fetch or other cancellable APIs.
  • On exhaustion, the last error is re-thrown unchanged.