Retry Budget With Total Deadline

A retry policy that stops when the total deadline is reached instead of retrying a fixed count, returning the last error and the attempt log.

JavascriptData Fetching

Code

export async function withRetryBudget(call, options) {
  const {
    deadlineMs,
    delayMs = 100,
    now = () => Date.now(),
    sleep = (ms) => new Promise((r) => setTimeout(r, ms)),
  } = options;
  if (!Number.isFinite(deadlineMs) || deadlineMs <= 0) {
    throw new RangeError("deadlineMs must be a positive number");
  }
  const start = now();
  const attempts = [];
  let lastError = null;

  while (true) {
    const elapsed = now() - start;
    if (elapsed >= deadlineMs) {
      return { ok: false, reason: "deadline-exceeded", attempts, lastError };
    }
    const attemptNo = attempts.length + 1;
    try {
      const value = await call(attemptNo);
      attempts.push({ attempt: attemptNo, ok: true });
      return { ok: true, value, attempts };
    } catch (err) {
      attempts.push({ attempt: attemptNo, ok: false, error: String(err) });
      lastError = err;
      const remaining = deadlineMs - (now() - start);
      if (remaining <= delayMs) {
        return { ok: false, reason: "deadline-exceeded", attempts, lastError };
      }
      await sleep(delayMs);
    }
  }
}