AniUI Academy

Retry with full jitter

mediumAsync & Promises+250 XPPractice

When an API goes down, every client fails at once and, with fixed backoff, retries at the same instant — a thundering herd that keeps the service down. AWS's 'full jitter' fix is to pick a random wait between zero and the backoff ceiling.

Implement retryWithJitter(task, options) where options may contain attempts (default 3), baseDelay (default 100) and random (default Math.random). Call task(attemptIndex) with the zero-based attempt number. Resolve with its value as soon as an attempt succeeds. After a failed attempt at index i, if further attempts remain, call random() exactly once and wait Math.floor(random() * baseDelay * 2 ** i) milliseconds before the next attempt. After the final attempt fails, reject with that last error and do not call random again.

Arguments

task(attemptIndex: number) => Promise<T>
The work to run, called with the zero-based attempt number.
options{ attempts?: number, baseDelay?: number, random?: () => number }
attempts defaults to 3, baseDelay to 100ms, random to Math.random.

Returns

Promise<T> — Resolves with the first successful attempt's value. Rejects with the final attempt's error after all retries are exhausted.

Examples

let calls = 0;
const value = await retryWithJitter(async () => {
  calls++;
  if (calls < 3) throw new Error("flaky");
  return "recovered";
}, { attempts: 5, baseDelay: 10, random: () => 0 });
// value === "recovered", calls === 3

Keeps trying until an attempt succeeds.

// With random: () => 1, waits scale: 20ms after attempt 0, 40ms after attempt 1.
try {
  await retryWithJitter(async () => { throw new Error("down"); }, {
    attempts: 3, baseDelay: 20, random: () => 1,
  });
} catch (error) {
  // error.message === "down" — final attempt's error
}

Ceiling doubles each round; actual wait is a random slice of it.

What it has to do

  • Call task with the zero-based attempt index.
  • Resolve as soon as an attempt succeeds, without waiting again.
  • Call random exactly once per wait, and never after the final attempt.
  • Wait Math.floor(random() * baseDelay * 2 ** attemptIndex) milliseconds between attempts.
  • Reject with the error from the final attempt.

Notes

  • The actual wait is Math.floor(random() * baseDelay * 2 ** attemptIndex) — the ceiling doubles each time, the real wait is a random slice of it.
  • Call random exactly once per wait, and don't call it after the last attempt — tests enforce both to catch off-by-one bugs.
Asked atAmazonStripe

Stuck? The javascript course covers everything this challenge needs.

Try it yourself
Loading playground...
5 tests · +250 XP

Discussion

Sign in to post
Sign in to join the conversation.Sign in

No comments yet. Be the first to say something.