Promises in JavaScript have no concept of time. When you use await
or attach a function with .then()
, it will wait until the Promise is either resolved or rejected. This is usually not a problem as most async tasks finish within a reasonable time and their result is needed.
But when a client is waiting for a response of, let's say, an HTTP server, it's better to return early with an error giving the caller a chance to retry rather than to wait for a potentially long time.
Fortunately, there is a built-in Promise helper function that can be used to add timeout functionality to any Promise-based construct.
The Promise.race is a global property of the Promise object. It gets an array of Promises and waits for the first one to finish. Whether the race is resolved or rejected depends on the winning member.
For example, the following code races two Promises. The second one resolves sooner, and the result of the other one is discarded:
const p1 =
new Promise((res) => setTimeout(() => res("p1"), 1000));
const p2 =
new Promise((res) => setTimeout(() => res("p2"), 500));
const result = await Promise.race([p1, p2]);
// result = p2
Similarly, it works for rejections also. If the winning Promise is rejected, the race is rejected:
const p1 =
new Promise((res) => setTimeout(() => res("p1"), 1000));
const p2 =
new Promise((_r, rej) => setTimeout(() => rej("p2"), 500));
try {
const result = await Promise.race([p1, p2]);
} catch(e) {
// e = p2
}
The arguments of the Promise.race
function are Promises. This makes it work with async
functions too:
const fn = async (time, label) => {
await new Promise((res) => setTimeout(res, time));
return label;
}
const result =
await Promise.race([fn(1000, "p1"), fn(500, "p2")])
// result = p2
Just don't forget to call the async
functions so that the race gets Promises. This can be a problem with anonymous functions and those need to be wrapped IIFE-style: