Promise.race()

Settled, not succeeded. A fast rejection beats a slow success, which makes race the natural way to impose a timeout and the wrong way to ask for the first working result.

Promise static methodES2015Live demo
Common call
await Promise.race([work, timeout(5000)])
Returns
the first settlement, win or lose
Replaces
manual timer plus flag bookkeeping
Watch out
an EMPTY array never settles — it hangs forever
Promise.race(iterableiterable — Any iterable. A non-promise value counts as already settled, so including one makes race resolve immediately with it.type: iterable · required)
→ Promise

Demo

Live evaluation
Try:
Inputs
jsonstringJSON list of [outcome, value, ms]
Output
await Promise.race(JSON.parse('[["ok","slow",50],["ok","fast",10]]').map(([k, v, ms]) => new Promise((res, rej) => setTimeout(() => k === 'ok' ? res(v) : rej(new Error(v)), ms))))
Pending…

Each input is [outcome, value, delay in ms]: 'ok' fulfils with the value, anything else rejects with an Error carrying it. Whatever settles first decides the outcome, win or lose. The second case is the one that matters: a rejection at 10ms beats a success at 50ms, so race is the right tool for a timeout and the wrong one for "try several sources and take whichever works" — that is Promise.any. One case is deliberately missing from this demo: an EMPTY list never settles at all, and a demo that waits forever is not a demo.

Parameters

NameTypeRequiredDescription
iterableiterableyesAny iterable. A non-promise value counts as already settled, so including one makes race resolve immediately with it.

Return value

Promise — A Promise that settles exactly as the first input to settle does — fulfilled if that one fulfilled, rejected if it rejected.

Common patterns

Impose a timeout
The canonical use.
const timeout = ms => new Promise((_, rej) =>
  setTimeout(() => rej(new Error("timeout")), ms));
await Promise.race([fetchData(), timeout(5000)]);
First SUCCESS instead
any ignores rejections; race does not.
await Promise.any([mirrorA(), mirrorB()]);
Cancel the loser too
race does not stop anything by itself.
const c = new AbortController();
try { await Promise.race([fetch(u, {signal: c.signal}), timeout(5000)]); }
finally { c.abort(); }

Examples

1. First to settle
await Promise.race([delay(50, "slow"), Promise.resolve("fast")])
Returns
'fast'
2. A fast rejection wins
await Promise.race([failAfter(1, new Error("early")), delay(50, "ok")])
Returns
throws Error: early
3. Empty array hangs
await Promise.race([])
Returns
never settles
4. A plain value wins instantly
await Promise.race([delay(50, "slow"), 42])
Returns
42
5. any ignores the rejection
await Promise.any([Promise.reject(new Error("a")), delay(5, "ok")])
Returns
'ok'
6. The loser keeps running
the slow fetch still completes, its result discarded
Returns
true

Pitfalls

1. An empty iterable never settles
Promise.race([]) returns a promise that stays pending forever, so an await on it hangs the function silently — no error, no timeout, no log. Promise.any([]) rejects immediately instead, which is at least visible.
Hangs
await Promise.race([])
the function never continues
Guard the empty case
if (!tasks.length) return [];
await Promise.race(tasks);
returns
2. A fast failure beats a slow success
This is the definition, not a bug — but it means race is wrong for "try several mirrors and take whichever works". One mirror failing quickly rejects the whole race while a working mirror is still in flight. Promise.any is the method for that.
Fast failure wins
await Promise.race([failsFast, succeedsSlowly])
throws
First success
await Promise.any([failsFast, succeedsSlowly])
the slow result
3. It does not cancel the losers
The timed-out request carries on to completion, still holding its connection and still able to produce an unhandled rejection later. A timeout without an AbortController limits how long you WAIT, not how long the work runs.
Still in flight
await Promise.race([fetch(url), timeout(1000)])
the fetch continues
Abort it
const c = new AbortController();
try { await Promise.race([fetch(url, {signal: c.signal}), timeout(1000)]); }
finally { c.abort(); }
cancelled
4. A non-promise in the array wins immediately
Plain values count as already settled, so one stray non-promise makes the race pointless — it resolves on the first microtask with that value. Easy to do by forgetting to call a function that returns a promise.
Resolves at once
await Promise.race([fetchData, timeout(5000)])
the fetchData FUNCTION, unresolved
Call it
await Promise.race([fetchData(), timeout(5000)])
a real race

When to use

Use it
  • Timeouts, paired with an AbortController
  • Taking the first response from several equivalent sources, when a failure should also abort
  • Waiting for whichever of two events happens first
Reach for something else
  • You want the first SUCCESS → Promise.any
  • You want every result → Promise.all or allSettled
  • The iterable may be empty → guard it, or it hangs
  • You need the losing work to stop → AbortController

Notes

Complexity
O(n) to attach handlers; wall-clock time is the FASTEST input
Return
A new Promise mirroring the first settlement
CPython impl
V8: Builtins-promise-race
Memory
Holds handlers on every input until one settles
Thread-safe
Single-threaded

FAQ

race settles on the first to FINISH either way; any resolves on the first to SUCCEED and only rejects if all of them fail. Use race for timeouts, any for redundancy.

Promise.race([work, timeout]);      // whichever first
Promise.any([mirrorA, mirrorB]);    // whichever works

History

ES2015
Promise.race added with Promise.all.
ES2020
Promise.any added, giving first-success semantics race could not express.