Promise.race() in JavaScript
Promise.race(iterable) takes an array (or any iterable) of promises and returns a new promise that fulfills or rejects as soon as the first of the passed promises settles. It does not matter whether it succeeded or failed: this is a race of promises in which whoever settles first decides the result.
Theory
TL;DR
Promise.race(iterable)returns a promise that mirrors the fate of the first settled promise.- The outcome can be either a success or a failure: it all depends on who finished first.
- The remaining promises are ignored but not cancelled, they keep running in the background.
- Plain values are automatically wrapped into
Promise.resolve(), so a non promise wins instantly. - The main practical scenario is a timeout for an operation that may hang.
Quick example
const fast = new Promise(resolve => setTimeout(() => resolve('Fast'), 1000));
const slow = new Promise(resolve => setTimeout(() => resolve('Slow'), 3000));
Promise.race([fast, slow]).then(console.log);The result after 1 second:
FastThe second promise (slow) keeps running in the background, but the result is already decided: fast won the race.
Syntax:
Promise.race(iterable)It returns a new Promise that settles with the value of the first successfully resolved promise, or fails with an error if a reject comes first.
How it works under the hood
- All promises start running at the same time (practically at the same time, since each one started when it was created).
- As soon as one of them settles (
resolveorreject):- the result of that promise becomes the result of the whole race;
Promise.race()moves into thefulfilledorrejectedstate;- the remaining promises are ignored (but not cancelled).
- Those remaining promises keep running in the background, yet their result affects nothing, because a promise changes state only once.
If the first promise settles with an error
const ok = new Promise(resolve => setTimeout(() => resolve('All good'), 2000));
const fail = new Promise((_, reject) => setTimeout(() => reject('Error!'), 1000));
Promise.race([ok, fail])
.then(console.log)
.catch(console.error);The result after 1 second:
Error!Even though ok will succeed later, the outcome of the whole race is already decided by the error. This is exactly where Promise.race() differs from Promise.any(), which ignores rejections and waits for the first successful promise.
A timeout built on Promise.race()
A common practice is to use Promise.race() to put a time limit on an operation.
function timeout(ms) {
return new Promise((_, reject) =>
setTimeout(() => reject(`Timed out after ${ms} ms`), ms)
);
}
const fetchData = new Promise(resolve =>
setTimeout(() => resolve('Response received'), 3000)
);
Promise.race([fetchData, timeout(2000)])
.then(console.log)
.catch(console.error);The output after 2 seconds:
Timed out after 2000 msThis is one of the most frequent uses of Promise.race(): capping operations that hang, network requests for example.
Instant promises and non promises
If every promise settles instantly, the first one in iteration order wins:
Promise.race([
Promise.resolve('First'),
Promise.resolve('Second'),
Promise.resolve('Third')
]).then(console.log); // FirstIf you pass a non promise, Promise.race() automatically wraps the plain value into Promise.resolve(), so it wins the race immediately:
Promise.race([42, Promise.resolve('OK')]).then(console.log); // 42How it differs from the other methods
| Method | What it does | Settles when | On failure |
|---|---|---|---|
Promise.all() | Waits for all promises | All have succeeded | Falls over on the first reject |
Promise.allSettled() | Waits for all of them, whatever the outcome | All are settled | Never falls over |
Promise.race() | Takes the first settled one | The first resolve or reject | Depends on the first promise |
Promise.any() | Takes the first successful one (resolve) | The first fulfilled | Falls over if all are rejected |
A simple analogy:
Picture a race. You have several couriers: whichever result arrives first is the one you accept. The others are still on their way, but you no longer care, you already got what you needed.
A summary of the method:
| Feature | Promise.race() |
|---|---|
| Accepts | An array (or any other iterable) of promises |
| Settles when | The first promise fulfills or rejects |
| Returns | The result (or error) of the first settled promise |
| The other promises | Ignored (but not cancelled) |
| Common use | Time limits (timeout), competing requests |
Common mistakes
- Thinking the losing promises get cancelled. They keep going: the request is still in flight, the timer still ticks, resources are still spent. Real cancellation needs an
AbortController. - Confusing
race()withany().race()fails if a rejected promise settled first, whereasany()waits for the first successful one. - Forgetting
.catch(). A race against a timeout can reject by design, and without a handler you get anunhandled promise rejection. - Passing an empty array.
Promise.race([])never settles, because there is simply no candidate to win. - Leaving the timer running after the main operation wins. The
setTimeoutfrom the timeout lives on and in Node.js keeps the process alive, so clear it withclearTimeoutin afinally.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.