typescript 51 lines · 10 steps

Retry with timeout and backoff in TypeScript

A generic helper that races each attempt against a timeout and retries with exponential backoff and jitter.

Explained by highlit
1type RetryOptions = {
2 retries?: number;
3 timeoutMs?: number;
4 baseDelayMs?: number;
5};
6 
7class TimeoutError extends Error {
8 constructor(ms: number) {
9 super(`Operation timed out after ${ms}ms`);
10 this.name = "TimeoutError";
11 }
12}
13 
14function withTimeout<T>(task: () => Promise<T>, ms: number): Promise<T> {
15 return new Promise<T>((resolve, reject) => {
16 const timer = setTimeout(() => reject(new TimeoutError(ms)), ms);
17 task().then(
18 (value) => {
19 clearTimeout(timer);
20 resolve(value);
21 },
22 (err) => {
23 clearTimeout(timer);
24 reject(err);
25 },
26 );
27 });
28}
29 
30const delay = (ms: number) => new Promise((r) => setTimeout(r, ms));
31 
32export async function retryWithTimeout<T>(
33 task: () => Promise<T>,
34 { retries = 3, timeoutMs = 5000, baseDelayMs = 200 }: RetryOptions = {},
35): Promise<T> {
36 let lastError: unknown;
37 
38 for (let attempt = 0; attempt <= retries; attempt++) {
39 try {
40 return await withTimeout(task, timeoutMs);
41 } catch (err) {
42 lastError = err;
43 if (attempt === retries) break;
44 const backoff = baseDelayMs * 2 ** attempt;
45 const jitter = Math.random() * baseDelayMs;
46 await delay(backoff + jitter);
47 }
48 }
49 
50 throw lastError;
51}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Wrapping a promise in a manual timeout lets you reject slow operations that would otherwise hang forever.
  2. 2Exponential backoff plus random jitter spreads out retries so concurrent clients don't all hammer a recovering service at once.
  3. 3Tracking the last error and rethrowing it after the loop preserves the real failure reason for the caller.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Retry with timeout and backoff in TypeScript — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code