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. When the winning branch should navigate away, follow the race with a JavaScript redirect in the then handler. 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)
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);
});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
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);
});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
(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")]),
),
);
})();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)
Both branches below use
JavaScript setTimeout() to create
one-shot Promise delays; the race changes which settlement is observed,
not how either timer is scheduled.
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));You should see one line logging catch: timeout after 100ms.
The 100ms rejection settles before the 2000ms fulfillment, so the catch branch runs. The slower setTimeout behind delay(2000, …) still fires unless you keep its handle and call clearTimeout—see clear all timeouts in JavaScript when timeout guards stack up during teardown. Branch on timeout substrings in error.message with JavaScript indexOf when you map rejection reasons to recovery handlers. In the browser, a failed race sometimes ends in location.reload() — see javascript location reload for why the legacy true flag no longer applies.
Empty iterable stays pending
const r = Promise.race([]);
let settled = false;
r.then(
() => {
settled = true;
},
() => {
settled = true;
},
);
setTimeout(() => {
console.log("after-50ms-settled=" + settled);
}, 50);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. Validate numeric strings from forms before they enter a race; see check if string is number when the iterable holds user input:
Promise.race([1, Promise.resolve(2), Promise.resolve(3)]).then((v) =>
console.log("race-mixed:", v),
);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
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.
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.
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.
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).
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.
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
