// Created on savesnippets.com ยท https://savesnippets.com/WsV9V2JMEQZqBN type RetryOpts = { attempts?: number; baseMs?: number; shouldRetry?: (err: unknown, attempt: number) => boolean; }; export async function retry(fn: () => Promise, opts: RetryOpts = {}): Promise { const attempts = opts.attempts ?? 5; const baseMs = opts.baseMs ?? 200; const should = opts.shouldRetry ?? (() => true); let lastErr: unknown; for (let i = 1; i <= attempts; i++) { try { return await fn(); } catch (e) { lastErr = e; if (i === attempts || !should(e, i)) throw e; const delay = baseMs * 2 ** (i - 1); const jitter = Math.random() * delay; await new Promise(r => setTimeout(r, delay + jitter)); } } throw lastErr; // unreachable, but TS wants it } const data = await retry(() => fetch('/api').then(r => r.json()), { attempts: 4, shouldRetry: (e) => !(e instanceof Response) || e.status >= 500, });