← Whiteboard questions

Retry a function with exponential backoff and jitter

Coding, Medium

Small real code that works when called late, twice, or with bad input.

Write retry(fn, options) in JavaScript (or the same idea in another language). fn is an async function that may fail. retry calls fn, and if it throws, waits and calls it again, up to retries more times (so at most 1 + retries calls). The wait before retry number k (k = 1, 2, ...) is a random time between 0 and min(maxMs, baseMs * 2^(k-1)). When fn succeeds, return its result. When all calls fail, throw the error of the last call. The options are retries (default 3), baseMs (default 100), maxMs (default 5000) and shouldRetry(error) (default: retry every error; if it returns false, stop at once and throw that error). Your function must be easy to test without real waiting: the options also include sleep(ms) (default: a real timer) and random() (default: Math.random), which a test can replace.

Examples

Input fn fails twice, then returns "ok". baseMs = 100, random always returns its top value (just under 1)
Output returns "ok" after 3 calls; the two waits were just under 100 ms and just under 200 ms
Why Wait 1 is up to min(5000, 100 x 1) and wait 2 is up to min(5000, 100 x 2). A random value just under 1 gives the top of each range.
Input fn always fails with a different message each call, retries = 3
Output throws the 4th error; fn was called 4 times
Why The first call plus 3 retries. The caller sees the last error.
Input fn fails with status 400, shouldRetry = (e) => e.status >= 500
Output throws at once after 1 call, with no wait
Why Edge case: some errors will never succeed on a retry, so waiting only wastes time.

Constraints

Your answer must contain

Retry a function with exponential backoff and jitter