Promise.any() in JavaScript
Promise.any(iterable) takes an array (or any iterable) of promises and returns a new promise that fulfills as soon as the first promise succeeds (resolve). It rejects only when every promise has rejected, so the logic reads as: "give me the first successful result and ignore the errors".
Theory
TL;DR
Promise.any(iterable)yields the value of the first successful promise.- Intermediate rejections are ignored, they do not stop the waiting.
- If all of them reject, the returned promise fails with an
AggregateError. err.errorsholds an array of every rejection reason.- Non promises are wrapped into
Promise.resolve(), so a plain value wins right away. - The classic scenario: a fallback across several data sources, or redundant API requests.
Quick example
const p1 = Promise.reject('Error 1');
const p2 = new Promise(resolve => setTimeout(() => resolve('Success 2'), 1000));
const p3 = new Promise(resolve => setTimeout(() => resolve('Success 3'), 2000));
Promise.any([p1, p2, p3])
.then(result => console.log('Result:', result))
.catch(err => console.error('Error:', err));The output after 1 second:
Result: Success 2The first successful one (p2) wins, even if the others are still running or will fail later.
Syntax:
Promise.any(iterable)It returns a new Promise that settles with the value of the first fulfilled promise, or rejects with the special AggregateError if every promise failed.
How it works under the hood
- All promises run in parallel, or more precisely each one has been running since it was created.
- If at least one of them succeeds (
resolve),Promise.any()settles immediately with that result. - If all of them turn out
rejected, an error of typeAggregateErroris returned with an array of all the reasons.
So a rejection does not end the waiting: the failure counter simply grows, and only when it matches the number of promises does catch fire.
If all promises reject
const p1 = Promise.reject('Error A');
const p2 = Promise.reject('Error B');
Promise.any([p1, p2])
.then(console.log)
.catch(err => console.error(err));Output:
AggregateError: All promises were rejectedThe error carries the list of all reasons:
err.errors // ['Error A', 'Error B']A closer look at how AggregateError behaves:
Promise.any([
Promise.reject('Error 1'),
Promise.reject('Error 2')
]).catch(err => {
console.error(err.name); // AggregateError
console.error(err.errors); // ['Error 1', 'Error 2']
});The order inside err.errors matches the order of the input promises, not the order in which they rejected.
A mix of successes and failures
const slow = new Promise(resolve => setTimeout(() => resolve('Slow success'), 2000));
const fail = Promise.reject('Error!');
const fast = new Promise(resolve => setTimeout(() => resolve('Fast success'), 500));
Promise.any([slow, fail, fast])
.then(console.log)
.catch(console.error);The result after 0.5 seconds:
Fast successEven though fail failed before anyone else, Promise.any() waits for the first successful promise.
Falling back across several sources
Imagine you have three servers and need data from the first one that answers successfully, even if the other two are unavailable:
const fetchFromA = fetch('/api/a'); // may fail
const fetchFromB = fetch('/api/b');
const fetchFromC = fetch('/api/c');
Promise.any([fetchFromA, fetchFromB, fetchFromC])
.then(res => console.log('Response received'))
.catch(() => console.error('All sources are unavailable'));That is how a fallback strategy is built: success from any source counts as enough.
How it differs from the other methods
| Method | What it returns | Settles when | Fails when |
|---|---|---|---|
Promise.all() | An array with every successful result | All promises are fulfilled | The first reject |
Promise.allSettled() | An array of statuses (fulfilled / rejected) | All promises are settled | Never |
Promise.race() | The first settled promise (success or failure) | Any resolve or reject | It settles on the first reject |
Promise.any() | The first successful result | The first fulfilled | When all are rejected |
In other words:
Promise.race()reacts to the first settled promise, failures included;Promise.any()reacts only to the first successful one.
A simple analogy:
Imagine you sent three couriers along different routes.
Promise.any()says: "whichever result arrives successfully first is the one I take, and the failures of the rest do not concern me".- Only if all of them get lost do you hear about an error.
Summary:
| Property | Value |
|---|---|
| What it does | Returns the first successfully fulfilled promise |
| If all are rejected | Returns an AggregateError with the reasons |
| Return type | Promise |
| Similar to | Promise.race(), but it ignores reject |
| Typical scenarios | Fallback requests, backup data sources, redundant API calls |
Common mistakes
- Expecting a plain error in
catch. What arrives is anAggregateError, and the concrete reasons live inerr.errors, soerr.messageon its own explains almost nothing. - Confusing it with
Promise.race().race()fails on the very first rejection, whileany()keeps waiting for a success. - Forgetting about support.
Promise.any()is ES2021: very old environments need a polyfill. - Thinking the losing requests get cancelled. The other promises keep running and keep loading the network, real cancellation needs an
AbortController. - Passing an empty array.
Promise.any([])rejects immediately with anAggregateError, because there is no candidate that could succeed. - Assuming
fetchrejects on a server error. A 500 response counts as success forfetch, so in a fallback scenario the status has to be checked by hand and the error thrown yourself.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.