Promise Combinators
When you need to work with several Promises at once, JavaScript gives you four combinator methods on the Promise class. Each one takes an iterable of Promises and returns a single Promise. What differs between them is the rule for when that returned Promise settles.
Waiting for All Promises with Promise.all()
Promise.all waits for all of the Promises to fulfill. If any one of them rejects, the returned Promise rejects right away with that error. This behavior is called fail-fast.
const p1 = fetch("/api/users");
const p2 = fetch("/api/posts");
const p3 = fetch("/api/comments");
Promise.all([p1, p2, p3])
.then(([users, posts, comments]) => {
console.log("All data loaded");
})
.catch((error) => {
console.error("One request failed:", error.message);
});
The results come back in the same order as the input array, no matter which Promise settles first. Use Promise.all when you need every result and a single failure should abort the whole operation.
const ids = [1, 2, 3];
const users = await Promise.all(ids.map((id) => fetchUser(id)));
// users = [user1, user2, user3] — same order as ids
Handling Partial Failures with Promise.allSettled()
Promise.allSettled waits for all of the Promises to settle, whether they fulfill or reject. It never short-circuits, so you always get the outcome of every Promise.
const results = await Promise.allSettled([
fetchUser(1),
fetchUser(-1), // Will reject
fetchUser(2),
]);
results.forEach((result) => {
if (result.status === "fulfilled") {
console.log("Success:", result.value);
} else {
console.log("Failed:", result.reason.message);
}
});
// "Success: { id: 1, name: 'Alice' }"
// "Failed: Invalid user ID"
// "Success: { id: 2, name: 'Alice' }"
Each result object has a status property, which is either "fulfilled" or "rejected", and then either a value property or a reason property. Use Promise.allSettled when you want to try all of the operations and handle the ones that fail.
First to Settle with Promise.race()
Promise.race returns the result of the first Promise to settle, whether that Promise fulfills or rejects.
const timeout = new Promise((_, reject) => {
setTimeout(() => reject(new Error("Timeout")), 5000);
});
const data = fetch("/api/data");
Promise.race([data, timeout])
.then((result) => console.log("Got data:", result))
.catch((error) => console.error(error.message)); // "Timeout" if fetch takes > 5s
The example above shows the common use for Promise.race: putting a timeout on a request.
First to Fulfill with Promise.any()
Promise.any returns the result of the first Promise to fulfill. Rejections are ignored unless all of the Promises reject.
const fastest = await Promise.any([
fetch("https://cdn1.example.com/data"),
fetch("https://cdn2.example.com/data"),
fetch("https://cdn3.example.com/data"),
]);
console.log("Fastest CDN responded:", fastest);
If all of the Promises reject, Promise.any rejects with an AggregateError that holds all of the individual errors:
try {
await Promise.any([
Promise.reject(new Error("Error 1")),
Promise.reject(new Error("Error 2")),
]);
} catch (error) {
console.log(error instanceof AggregateError); // true
console.log(error.errors); // [Error: Error 1, Error: Error 2]
}
Comparison
| Method | Resolves when… | Rejects when… | Use case |
|---|---|---|---|
all |
All fulfill | Any rejects (fail-fast) | Need all results |
allSettled |
All settle | Never | Handle partial failures |
race |
First settles | First settles (if reject) | Timeouts, first response |
any |
First fulfills | All reject | Fastest successful result |