JavaScript Promise.race (first settled promise)

Tech reviewed: Deepak Prasad
JavaScript Promise.race (first settled promise)

Promise.race() is a static Promise concurrency helper: given an iterable of inputs (usually other Promises), it returns a single Promise that settles—fulfills or rejects—the same way as whichever input settles first in real time, which is not always the left-most slot in your array literal.

Use it for timeouts (race slow work against a timer), fastest replica (two fetch calls to different regions), or first user gesture among several async sources. For “wait for all to succeed,” use Promise.all instead. For “first success only,” compare Promise.any in the FAQ below. To create already-resolved inputs for demos, see Promise.resolve.


Basic Promise.race (fulfill wins)

javascript
const promise1 = new Promise((resolve) => {
  setTimeout(() => resolve("First promise resolved"), 500);
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => reject("Second promise rejected"), 1000);
});

Promise.race([promise1, promise2])
  .then((result) => {
    console.log(result);
  })
  .catch((error) => {
    console.log(error);
  });
Output

You should see one line logging First promise resolved.

Node.js v20.18.2 output after both timers complete; the 500ms branch fulfills first, so the then path runs.


When rejection settles first

javascript
const promise1 = new Promise((resolve) => {
  setTimeout(() => resolve("First promise resolved"), 1000);
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => reject("Second promise rejected"), 500);
});

const timeout = new Promise((resolve, reject) => {
  setTimeout(() => reject("Promise timed out"), 750);
});

Promise.race([promise1, promise2, timeout])
  .then((result) => {
    console.log(result);
  })
  .catch((error) => {
    console.log(error);
  });
Output

You should see one line logging Second promise rejected.

The 500ms rejection from promise2 settles before the 750ms timeout and before promise1 resolves, so the catch path runs with that reason. (The original draft text incorrectly blamed the timeout; the timers above match the code.)


Promise.race vs Promise.all: first settled vs all must fulfill

javascript
(async () => {
  const p1 = Promise.resolve(42);
  const p2 = Promise.resolve("Hello World");
  const p3 = Promise.reject("Oops");
  console.log(await Promise.race([p1, p2, p3]));
  try {
    await Promise.all([
      Promise.resolve(42),
      Promise.resolve("Hello World"),
      Promise.reject("Oops"),
    ]);
  } catch (e) {
    console.log(e);
  }
  console.log(
    JSON.stringify(
      await Promise.all([Promise.resolve(42), Promise.resolve("Hello World")]),
    ),
  );
})();
Output

You should see 3 lines, in order: 42, Oops, [42,"Hello World"].

Promise.race follows the first settled input (here 42 from an immediately fulfilled Promise.resolve). Promise.all rejects with the first rejection when any input rejects; when all fulfill, you get an array of values.


Timeout pattern (race slow work vs deadline)

javascript
function delay(ms, value) {
  return new Promise((resolve) => setTimeout(() => resolve(value), ms));
}
function timeout(ms) {
  return new Promise((_, reject) =>
    setTimeout(() => reject(new Error("timeout after " + ms + "ms")), ms),
  );
}

Promise.race([delay(2000, "data"), timeout(100)])
  .then((v) => console.log("ok:", v))
  .catch((e) => console.log("catch:", e.message));
Output

You should see one line logging catch: timeout after 100ms.

The 100ms rejection settles before the 2000ms fulfillment, so the catch branch runs.


Empty iterable stays pending

javascript
const r = Promise.race([]);
let settled = false;
r.then(
  () => {
    settled = true;
  },
  () => {
    settled = true;
  },
);
setTimeout(() => {
  console.log("after-50ms-settled=" + settled);
}, 50);
Output

You should see one line logging after-50ms-settled=false.

Node.js v20.18.2: after 50ms the Promise.race([]) result still had not settled.


Non-Promise values in the iterable

If an element is not a Promise, it is treated as already fulfilled with that value and can win immediately if it appears first:

javascript
Promise.race([1, Promise.resolve(2), Promise.resolve(3)]).then((v) =>
  console.log("race-mixed:", v),
);
Output

You should see one line logging race-mixed: 1.


Summary

Promise.race is the right tool when you care about whichever async branch finishes first, whether it succeeds or fails—promise race / promise.race() traffic usually maps to timeouts, lowest-latency backends, or first response UX. It does not cancel siblings, it does stay pending forever on an empty iterable, and it is not the same as Promise.all (all must fulfill) or Promise.any (first fulfillment only). Pair races with AbortController when you need real cancellation of fetch.

Frequently Asked Questions

1. What does Promise.race do in JavaScript?

Promise.race takes an iterable of inputs (usually Promises) and returns a single Promise that settles the same way as the first input that settles—if that input fulfills, the result fulfills; if it rejects, the result rejects.

2. What is the difference between Promise.race and Promise.all?

Promise.all waits for every input to fulfill and returns an array of values, or rejects immediately on the first rejection. Promise.race settles as soon as any one input settles, no matter how the others finish later.

3. What is the difference between Promise.race and Promise.any?

Promise.any waits for the first fulfillment and only rejects if every input rejects. Promise.race settles on the first settlement even if that is a rejection.

4. Does Promise.race cancel the other promises?

No. The other async work keeps running unless you wire cancellation yourself (AbortController, flags, or dismissing results when a faster branch wins).

5. What happens if you pass an empty array to Promise.race?

The returned Promise never settles—it stays pending forever because there is no first settlement.

6. Does Promise.race use the first promise in the array?

Not by index alone. It uses whichever input settles first in time. If several are already settled when race runs, the engine picks the first already-settled value in iterable order (see MDN).

References

Promise.race() - JavaScript | MDN
Promise.any() - JavaScript | MDN
Promise.all() - JavaScript | MDN

Olorunfemi Akinlua

Boasting over five years of experience in JavaScript, specializing in technical content writing and UX design. With a keen focus on programming languages, he crafts compelling content and designs user-friendly interfaces to enhance digital …

  • JavaScript
  • Web Design