Загрузка…
Загрузка…
Production fetch: retry только 429/5xx, full jitter backoff, honour Retry-After, cap attempts. Proactive pacing по RateLimit-* headers.
// Production-grade fetch with retry, full jitter, Retry-After, and a cap.
async function fetchRetry(url, opts = {}, { retries = 4, base = 300, cap = 10_000 } = {}) {
for (let attempt = 0; ; attempt++) {
try {
const res = await fetch(url, opts);
// Retry only server errors + throttling. 4xx (except 429) is terminal.
if (res.status === 429 || (res.status >= 500 && res.status !== 501)) {
if (attempt >= retries) return res; // give up, surface it
const ra = res.headers.get('Retry-After');
await sleep(ra ? retryAfterMs(ra) // server told us when
: jitter(base, cap, attempt)); // else full jitter backoff
continue;
}
return res; // success or terminal 4xx
} catch (err) { // network error / timeout
if (attempt >= retries) throw err;
await sleep(jitter(base, cap, attempt));
}
}
}
// Full jitter: sleep a RANDOM amount up to the exponential ceiling.
const jitter = (base, cap, attempt) =>
Math.random() * Math.min(cap, base * 2 ** attempt);
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));// Pace yourself PROACTIVELY off RateLimit headers instead of waiting to be 429'd.
const remaining = Number(res.headers.get('RateLimit-Remaining'));
const resetMs = Number(res.headers.get('RateLimit-Reset')) * 1000;
if (remaining < 5) await sleep(resetMs - Date.now()); // slow down before the wallRetry с jitter + respect Retry-After; 4xx (кроме 429) terminal; proactive pacing лучше, чем ждать 429.
