Retry async operations with jittered backoff

JavaScript
// Retry an async task with exponential backoff, capped delay and jitter.
async function retry(task, { retries = 3, baseDelay = 200, maxDelay = 5000 } = {}) {
  let attempt = 0;
  for (;;) {
    try {
      return await task(attempt);
    } catch (error) {
      if (attempt >= retries) throw error;
      const ceiling = Math.min(maxDelay, baseDelay * 2 ** attempt);
      await new Promise((resolve) => setTimeout(resolve, Math.random() * ceiling));
      attempt += 1;
    }
  }
}

// Only retry 503 and 429 responses; other status codes reject immediately.
const fetchHealth = () => fetch('/api/health').then((response) => {
  if (response.status === 503 || response.status === 429) {
    throw new Error(`temporary status ${response.status}`);
  }
  return response.json();
});

retry(fetchHealth, { retries: 4 }).then(console.log).catch(console.error);