Promise.any
+ AggregateError
ECMAScript proposal: Author: Mathias Bynens, Kevin Gibbons, Sergey Rubanov
Champion: Mathias Bynens
Stage: Stage 4 of the TC39 process.
Motivation
There are four main combinators in the Promise
landscape.
name | description | |
---|---|---|
Promise.allSettled |
does not short-circuit | added in ES2020 |
Promise.all |
short-circuits when an input value is rejected | added in ES2015 |
Promise.race |
short-circuits when an input value is settled | added in ES2015 |
Promise.any |
short-circuits when an input value is fulfilled | this proposal |
These are all commonly available in userland promise libraries, and theyโre all independently useful, each one serving different use cases.
Proposed solution
Promise.any
accepts an iterable of promises and returns a promise that is fulfilled by the first given promise to be fulfilled, or rejected with an AggregateError
holding the rejection reasons if all of the given promises are rejected. (If something more fundamental goes wrong, e.g. iterating over the iterable results in an exception, Promise.any
returns a rejected promise with that exception.)
High-level API
try {
const first = await Promise.any(promises);
// Any of the promises was fulfilled.
} catch (error) {
// All of the promises were rejected.
}
Or, without async
/await
:
Promise.any(promises).then(
(first) => {
// Any of the promises was fulfilled.
},
(error) => {
// All of the promises were rejected.
}
);
In the above examples, error
is an AggregateError
, a new Error
subclass that groups together individual errors. Every AggregateError
instance contains a pointer to an array of exceptions.
FAQ
any
?
Why choose the name It clearly describes what it does, and thereโs precedent for the name any
in userland libraries offering this functionality:
- https://github.com/kriskowal/q#combination
- http://bluebirdjs.com/docs/api/promise.any.html
- https://github.com/m0ppers/promise-any
- https://github.com/cujojs/when/blob/master/docs/api.md#whenany
- https://github.com/sindresorhus/p-any
AggregateError
instead of an array?
Why throw an The prevailing practice within the ECMAScript language is to only throw exception types. Existing code in the ecosystem likely relies on the fact that currently, all exceptions thrown by built-in methods and syntax are instanceof Error
. Adding a new language feature that can throw a plain array would break that invariant, and could be a web compatibility issue. Additionally, by using an Error
instance (or a subclass), a stack trace can be provided โ something thatโs easy to discard if not needed, but impossible to obtain later if it is needed.
Illustrative examples
This snippet checks which endpoint responds the fastest, and then logs it.
Promise.any([
fetch('https://v8.dev/').then(() => 'home'),
fetch('https://v8.dev/blog').then(() => 'blog'),
fetch('https://v8.dev/docs').then(() => 'docs')
]).then((first) => {
// Any of the promises was fulfilled.
console.log(first);
// โ 'home'
}).catch((error) => {
// All of the promises were rejected.
console.log(error);
});
TC39 meeting notes
- March 2019
- June 2019
- July 2019
- October 2019 part one and part two
- June 2020 part one and part two
- July 2020
Specification
Implementations
-
JavaScript engines:
- JavaScriptCore, shipping in Safari 14
- SpiderMonkey, shipping in Firefox 79
- V8, shipping in Chrome 85
- XS
- engine262
-
Polyfills: