Promise.any()

The redundancy combinator. Where race settles on the first thing to finish, any waits for something to actually work — which is what you almost always mean when you have several equivalent sources.

Promise static methodES2020Live demo
Common call
await Promise.any(mirrors.map(fetch))
Returns
the first successful result
Replaces
a chain of catch-and-retry fallbacks
Watch out
all failing gives an AggregateError, not the last error
Promise.any(iterableiterable — Any iterable of promises. An empty iterable rejects immediately with an AggregateError — unlike race, which would hang.type: iterable · required)
→ Promise

Demo

Live evaluation
Try:
Inputs
jsonstringJSON list of [outcome, value, ms]
Output
await Promise.any(JSON.parse('[["err","a",10],["ok","ok",30]]').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. The first case is the contrast with race: the rejection at 10ms does not end the wait, and the success at 30ms wins. Only when EVERY input rejects does any reject, with an AggregateError whose own message is generic — the real reasons are in its errors array. An empty list rejects immediately, which is the sensible opposite of Promise.race([]), which hangs forever.

Parameters

NameTypeRequiredDescription
iterableiterableyesAny iterable of promises. An empty iterable rejects immediately with an AggregateError — unlike race, which would hang.

Return value

Promise — A Promise for the first FULFILLED result. Rejects only if every input rejects, with an AggregateError whose errors property holds all the reasons.

Common patterns

Try several mirrors
The canonical use — first one that works.
const data = await Promise.any(mirrors.map(u => fetch(u)));
Read the collected errors
AggregateError carries every reason.
try { await Promise.any(tasks); }
catch (e) { console.error(e.errors); }
Timeouts need race
any would ignore the timeout rejection.
await Promise.race([work, timeout(5000)]);

Examples

1. First success wins
await Promise.any([Promise.reject(new Error("a")), delay(5, "ok")])
Returns
'ok'
2. Rejections ignored
await Promise.any([failsFast, succeedsSlowly])
Returns
the slow result
3. All reject
await Promise.any([Promise.reject(new Error("a")), Promise.reject(new Error("b"))])
Returns
throws AggregateError: All promises were rejected
4. The reasons are kept
Promise.any([Promise.reject(new Error("a"))]).catch(e => e.errors.length)
Returns
1
5. Empty rejects at once
await Promise.any([])
Returns
throws AggregateError
6. race would hang
await Promise.race([])
Returns
never settles

Pitfalls

1. The rejection is an AggregateError, not your error
When everything fails you get a wrapper, and error-handling code that reads e.message finds the generic "All promises were rejected" rather than anything useful. The real reasons are in e.errors, an array in input order.
Generic message
catch (e) { log(e.message); }
'All promises were rejected'
Read .errors
catch (e) { log(e.errors.map(x => x.message)); }
['a', 'b']
2. It hides failures you might want to know about
If the first mirror is permanently broken and the second always works, any succeeds every time and nothing reports the broken one. Log e.errors, or inspect with allSettled, if partial failure is worth an alert.
Silent
await Promise.any([brokenMirror, goodMirror])
succeeds, broken one unreported
See everything
const rs = await Promise.allSettled([brokenMirror, goodMirror]);
both outcomes visible
3. It is not a timeout mechanism
Because a rejection does not end the wait, racing work against a rejecting timer does nothing — any simply ignores the timeout and keeps waiting for the work. Timeouts need race.
Timeout ignored
await Promise.any([slowWork, timeout(100)])
waits for slowWork
Use race
await Promise.race([slowWork, timeout(100)])
rejects at 100ms
4. ES2020 — check your runtime
Node 15+ and 2020-era browsers, slightly later than allSettled. AggregateError arrived with it, so a polyfill needs both.
Missing
Promise.any(tasks)
TypeError: Promise.any is not a function
Invert with all
Promise.all(tasks.map(p => p.then(v => Promise.reject(v), e => e)))
  .then(errs => Promise.reject(errs), v => v)
the same semantics, inverted

When to use

Use it
  • Several equivalent sources — mirrors, replicas, CDNs
  • Fallback chains where any one success is enough
  • Racing a cache against a network fetch
Reach for something else
  • Timeouts → Promise.race, which respects rejections
  • You need every result → Promise.all or allSettled
  • You need to know about the failures that were tolerated → allSettled
  • Targeting runtimes older than 2020 → the inverted-all idiom

Notes

Complexity
O(n); wall-clock time is the fastest SUCCESS, or the slowest input if all fail
Return
A new Promise; rejects with an AggregateError only if every input rejects
CPython impl
V8: Builtins-promise-any
Memory
Collects rejection reasons until a success arrives or all fail
Thread-safe
Single-threaded

FAQ

any for redundancy — first thing that WORKS. race for timeouts — first thing that FINISHES. The distinction only shows up when something fails quickly, which is exactly the case you are guarding against.

History

ES2020
Promise.any added with AggregateError, alongside Promise.allSettled.