retryAsyncWithJitteredBackoff

JavaScript
// Retries an async operation with capped exponential backoff and full jitter.
export async function retryAsync(fn, {
  retries = 5,
  baseMs = 100,
  capMs = 5_000,
  shouldRetry = () => true,
} = {}) {
  if (!Number.isInteger(retries) || retries < 0) {
    throw new RangeError('retries must be a non-negative integer');
  }
  for (let attempt = 0; ; attempt += 1) {
    try {
      return await fn(attempt); // pass the attempt number to the operation
    } catch (error) {
      if (attempt >= retries || !shouldRetry(error, attempt)) throw error;
      const ceiling = Math.min(capMs, baseMs * 2 ** attempt);
      const delayMs = Math.random() * ceiling; // full jitter
      await new Promise((resolve) => setTimeout(resolve, delayMs));
    }
  }
}