Retry with Exponential Backoff

Retrying a failed, idempotent operation with an increasing delay between attempts, to avoid hammering a struggling server.

Purpose

Retrying a failed request can recover from a transient failure, but retrying immediately and repeatedly can worsen an already-struggling server. Exponential backoff doubles (or otherwise increases) the delay between attempts, spacing retries out over time.

Example

async function fetchWithRetry(url, retries = 3, delayMs = 300) {
  for (let attempt = 0; attempt <= retries; attempt++) {
    try {
      const response = await fetch(url);
      if (response.ok) return response;
    } catch {
      // network error β€” fall through to retry
    }
    if (attempt < retries) {
      await new Promise((resolve) => setTimeout(resolve, delayMs * 2 ** attempt));
    }
  }
  throw new Error(`Failed after ${retries + 1} attempts.`);
}

Behavior notes

  • Only retry an idempotent operation β€” one where repeating it has the same effect as doing it once. A GET is normally safe to retry; a POST that creates or charges something is not, unless the server explicitly supports safe retry (an idempotency key, for instance).
  • Adding random β€œjitter” to each delay (rather than a purely deterministic backoff) helps avoid many clients retrying in synchronized bursts after a shared outage.
  • A maximum retry count and a maximum total delay both matter β€” unbounded retries can leave a user waiting indefinitely for an operation that will never succeed.

Common mistakes

  • Retrying a non-idempotent operation (a payment, an account-creation request) without server-side protection against duplicate effects.
  • Retrying immediately, repeatedly, with no backoff β€” can turn a brief server hiccup into a self-inflicted overload.

Runtime notes

A coding pattern built on core ECMAScript async features, behaving identically anywhere JavaScript runs.

Taught in these lessons

Updated 2026-09-03