How to add a timeout to a promise
Short answer
Promise.race() settles with the first promise that finishes. Race the task against a timer that rejects:
function withTimeout(promise, ms) {
let timer;
const timeout = new Promise((_, reject) => {
timer = setTimeout(() => reject(new Error(`Timed out after ${ms} ms`)), ms);
});
return Promise.race([promise, timeout]).finally(() => clearTimeout(timer));
}
const delay = (ms, value) => new Promise((resolve) => setTimeout(() => resolve(value), ms));
console.log(await withTimeout(delay(50, 'fast answer'), 200));
try {
await withTimeout(delay(500, 'slow answer'), 200);
} catch (error) {
console.log(error.message);
}Output:
fast answer
Timed out after 200 msFor fetch
For requests, pass a signal: then the request itself is cancelled, not just the waiting:
const response = await fetch('/api/report', { signal: AbortSignal.timeout(3000) });Common mistake
The timeout only stops the waiting. The slow operation itself keeps working in the background, and its result is simply ignored:
const slow = new Promise((resolve) =>
setTimeout(() => {
console.log('the slow task still finished');
resolve();
}, 300),
);
await Promise.race([slow, new Promise((resolve) => setTimeout(resolve, 100))]);
console.log('stopped waiting');
await new Promise((resolve) => setTimeout(resolve, 300));Output:
stopped waiting
the slow task still finishedFunctions used in this recipe
- Promise.race — Returns the result of the first promise to settle — successful or not.
- fetch — Sends an HTTP request and returns a promise of the response.