Skip to main content

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

javascript
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:

javascript
Fast

The second promise (slow) keeps running in the background, but the result is already decided: fast won the race.

Syntax:

javascript
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

  1. All promises start running at the same time (practically at the same time, since each one started when it was created).
  2. As soon as one of them settles (resolve or reject):
    • the result of that promise becomes the result of the whole race;
    • Promise.race() moves into the fulfilled or rejected state;
    • the remaining promises are ignored (but not cancelled).
  3. 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

javascript
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:

javascript
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.

javascript
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:

javascript
Timed out after 2000 ms

This 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:

javascript
Promise.race([ Promise.resolve('First'), Promise.resolve('Second'), Promise.resolve('Third') ]).then(console.log); // First

If you pass a non promise, Promise.race() automatically wraps the plain value into Promise.resolve(), so it wins the race immediately:

javascript
Promise.race([42, Promise.resolve('OK')]).then(console.log); // 42

How it differs from the other methods

MethodWhat it doesSettles whenOn failure
Promise.all()Waits for all promisesAll have succeededFalls over on the first reject
Promise.allSettled()Waits for all of them, whatever the outcomeAll are settledNever falls over
Promise.race()Takes the first settled oneThe first resolve or rejectDepends on the first promise
Promise.any()Takes the first successful one (resolve)The first fulfilledFalls 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:

FeaturePromise.race()
AcceptsAn array (or any other iterable) of promises
Settles whenThe first promise fulfills or rejects
ReturnsThe result (or error) of the first settled promise
The other promisesIgnored (but not cancelled)
Common useTime 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() with any(). race() fails if a rejected promise settled first, whereas any() waits for the first successful one.
  • Forgetting .catch(). A race against a timeout can reject by design, and without a handler you get an unhandled 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 setTimeout from the timeout lives on and in Node.js keeps the process alive, so clear it with clearTimeout in a finally.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.