javascript 44 lines · 8 steps

Retrying fetch with exponential backoff

A resilient fetch wrapper that retries transient failures with exponential backoff, jitter, and abort support.

Explained by highlit
1const RETRIABLE_STATUS = new Set([408, 429, 500, 502, 503, 504]);
2 
3function sleep(ms, signal) {
4 return new Promise((resolve, reject) => {
5 const id = setTimeout(resolve, ms);
6 signal?.addEventListener('abort', () => {
7 clearTimeout(id);
8 reject(new DOMException('Aborted', 'AbortError'));
9 }, { once: true });
10 });
11}
12 
13export async function fetchWithRetry(url, options = {}, config = {}) {
14 const {
15 retries = 4,
16 baseDelay = 300,
17 maxDelay = 8000,
18 signal,
19 } = config;
20 
21 let attempt = 0;
22 
23 while (true) {
24 try {
25 const response = await fetch(url, { ...options, signal });
26 
27 if (!RETRIABLE_STATUS.has(response.status) || attempt >= retries) {
28 return response;
29 }
30 
31 const retryAfter = Number(response.headers.get('retry-after'));
32 var waitBase = Number.isFinite(retryAfter) && retryAfter > 0
33 ? retryAfter * 1000
34 : Math.min(baseDelay * 2 ** attempt, maxDelay);
35 } catch (err) {
36 if (err.name === 'AbortError' || attempt >= retries) throw err;
37 waitBase = Math.min(baseDelay * 2 ** attempt, maxDelay);
38 }
39 
40 const jitter = Math.random() * waitBase * 0.3;
41 await sleep(waitBase + jitter, signal);
42 attempt++;
43 }
44}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Only retry on status codes and errors that are genuinely transient, and always cap the total number of attempts.
  2. 2Exponential backoff plus random jitter spreads retries out and prevents synchronized retry storms against a struggling server.
  3. 3Honoring both a server's Retry-After header and a caller's AbortSignal makes a retry loop cooperative rather than blindly aggressive.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Retrying fetch with exponential backoff — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code