Skip to main content

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.errors holds 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

javascript
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:

javascript
Result: Success 2

The first successful one (p2) wins, even if the others are still running or will fail later.

Syntax:

javascript
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

  1. All promises run in parallel, or more precisely each one has been running since it was created.
  2. If at least one of them succeeds (resolve), Promise.any() settles immediately with that result.
  3. If all of them turn out rejected, an error of type AggregateError is 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

javascript
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:

javascript
AggregateError: All promises were rejected

The error carries the list of all reasons:

javascript
err.errors // ['Error A', 'Error B']

A closer look at how AggregateError behaves:

javascript
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

javascript
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:

javascript
Fast success

Even 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:

javascript
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

MethodWhat it returnsSettles whenFails when
Promise.all()An array with every successful resultAll promises are fulfilledThe first reject
Promise.allSettled()An array of statuses (fulfilled / rejected)All promises are settledNever
Promise.race()The first settled promise (success or failure)Any resolve or rejectIt settles on the first reject
Promise.any()The first successful resultThe first fulfilledWhen 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:

PropertyValue
What it doesReturns the first successfully fulfilled promise
If all are rejectedReturns an AggregateError with the reasons
Return typePromise
Similar toPromise.race(), but it ignores reject
Typical scenariosFallback requests, backup data sources, redundant API calls

Common mistakes

  • Expecting a plain error in catch. What arrives is an AggregateError, and the concrete reasons live in err.errors, so err.message on its own explains almost nothing.
  • Confusing it with Promise.race(). race() fails on the very first rejection, while any() 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 an AggregateError, because there is no candidate that could succeed.
  • Assuming fetch rejects on a server error. A 500 response counts as success for fetch, so in a fallback scenario the status has to be checked by hand and the error thrown yourself.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.