Retry an async operation with exponential backoff
Retry a failing promise-returning function with growing, jittered delays and a retry predicate.
- JavaScript
- GreenSnippets
- MIT
- Executed with Node.js 22
async function retry(fn, { retries = 3, baseDelay = 200, maxDelay = 5000, shouldRetry = () => true } = {}) {
let attempt = 0;
for (;;) {
try {
return await fn(attempt);
} catch (error) {
if (attempt >= retries || !shouldRetry(error)) throw error;
const exp = Math.min(maxDelay, baseDelay * 2 ** attempt);
const delay = Math.random() * exp; // "full jitter"
await new Promise((resolve) => setTimeout(resolve, delay));
attempt++;
}
}
}How it works
Transient failures (timeouts, 502/503, rate limits) are often fixed by waiting and trying again. Delays double each attempt up to maxDelay, with random jitter so many clients do not retry in lockstep.
Only retry idempotent operations, or ones protected by an idempotency key. Retrying a non-idempotent POST can duplicate side effects. Use shouldRetry to avoid retrying permanent errors such as 400 or 404.
Related snippets
- PHP Tested
Retry a callable with exponential backoff in PHP
Re-run flaky operations with growing delays and jitter, only for chosen exception types.
- TypeScript Tested
Typed fetch helper that checks the response
A fetch wrapper that throws on HTTP errors and validates the JSON body with a type guard.
- JavaScript Tested
Verify a webhook signature in Node.js
Validate an HMAC-SHA256 webhook against the raw request body with a constant-time comparison and replay window.
- PHP Tested
Decode JSON safely in PHP
Parse JSON with exceptions on error and a depth limit, instead of silently getting null.

