Promise combinators explained
Promises added to ECMAScript 2015 specification gave us a new way of dealing with deferred computations. In “From callback hell
through promises to
async functions” I explained the differences between the common methods of dealing with asynchronous code. Today I would like to go through Promise
combinators — static methods that take compound promises as an argument.

Prerequisite — basic promise states and methods
The then
, catch
, finally
, resolve
and reject
methods should already feel comfortable by now but just in case you need a refresher, let’s have a quick glance at this simple example.
fetch("https://api.github.com/users/pawelgrzybek")
.then(data => data.json())
.then(dataJSON => console.log(dataJSON))
.catch(error => console.error(error))
.finally(() => console.log("Finally resolved / rejected! Uff!"));
Promise object like this can live in four distinguished states:
- “pending” - still waiting
- “fulfilled” - promise succeeded
- “rejected” - promise failed
- “settled” - succeeded or failed
To orchestrate its lifecycle, promises are using a few methods from it’s prototype:
then
- adds callback to fulfilled promisecatch
- adds callback to rejected promisefinally
- adds callback to settled promise
Of course all of these are much more complicated, but to comfortably grasp the concepts explained in the rest of this article this should be more than enough.
Four combinators
The Promise.all
and Promise.race
are part of a JavaScript since 2015. In 2020 Promise.allSettled
joins the gang. Promise.any
is nearly there (TBC).
Promise.all
- ES 2015Promise.race
- ES 2015Promise.allSettled
- ES 2020 (proposal)Promise.any
- TBC (proposal)
Promise.all()
Use Promise.all
when you want to check whether all promises fulfilled or if one of them rejected.
Promise.all([
fetch("https://api.github.com/users/pawelgrzybek").then(data => data.json()),
fetch("https://api.github.com/users/gabriel403").then(data => data.json())
])
.then(data =>
console.log(`Cool dudes are: ${data.map(dude => dude.name).join(" and ")}`)
)
.catch(error => console.error(error));
Promise.race()
Use Promise.race
when you want to handle the first fulfilled promise or if one of them rejected.
Promise.race([
fetch("https://api.github.com/users/pawelgrzybek").then(data => data.json()),
fetch("https://api.github.com/users/danjordan").then(data => data.json())
])
.then(data => console.log(`The winner is: ${data.name}`))
.catch(error => console.error(error));
Promise.allSettled()
Use Promise.allSettled
when you want to handle when all promises are settled regardless of the result (fulfilled or rejected). Look ma, no catch!
Promise.allSettled([
fetch("https://api.github.com/users/pawelgrzybek").then(data => data.json()),
fetch("https://api.github.com/users/danjordan").then(data => data.json())
])
.then(result => console.log(`All settled`));
Promise.any()
Use Promise.any
when you want to handle the first promise that fulfills. In contrast to Promise.race
it doesn’t reject when one of the promises fail.
Promise.any([
fetch("https://api.github.com/users/pawelgrzybek").then(data => data.json()),
fetch("https://api.github.com/users/gabriel403").then(data => data.json())
])
.then(result => console.log(`Cool dude is: ${result.name}`))
.catch(error => console.error(error));
To recapitulate
Hopefully this article helped you out. This should help pull it all together for you.
