Retry an async function with exponential backoff. API and network resilience.
export async function retryWithBackoff<T>(
fn: () => Promise<T>,
options: { maxAttempts?: number; baseMs?: number; maxMs?: number } = {}
): Promise<T> {
const { maxAttempts = 3, baseMs = 1000, maxMs = 10000 } = options;
let lastError: unknown;
for (let attempt = 0; attempt < maxAttempts; attempt++) {
try {
return await fn();
} catch (e) {
lastError = e;
if (attempt === maxAttempts - 1) throw e;
const delay = Math.min(baseMs * Math.pow(2, attempt), maxMs);
await new Promise((r) => setTimeout(r, delay));
}
}
throw lastError;
}
Calls fn(); on failure waits with exponential backoff (baseMs * 2^attempt, capped at maxMs) then retries. Default 3 attempts.
Wrap fetch or any async call that may transiently fail.
await retryWithBackoff(() => fetch(url).then(r => r.json()));