Promise Combinators
Promise.all, allSettled, race, and any give you different ways to coordinate many async operations.
Promise combinators coordinate many promises. The important choice is whether one failure should stop everything or whether you still need every result.
Promise.all runs promises in parallel and resolves when all fulfill. It rejects on the first rejection. Promise.allSettled waits for every promise, which is safer when partial failure is acceptable.
async function parallel() {
const [users, posts, tags] = await Promise.all([
fetch("/api/users").then((r) => r.json()),
fetch("/api/posts").then((r) => r.json()),
fetch("/api/tags").then((r) => r.json()),
]);
console.log(users.length, posts.length, tags.length);
}
async function safeParallel(ids) {
const results = await Promise.allSettled(ids.map((id) => fetchUser(id)));
const ok = results.filter((r) => r.status === "fulfilled");
const failed = results.filter((r) => r.status === "rejected");
console.log(`${ok.length} ok, ${failed.length} failed`);
}Promise.race settles as soon as any promise settles. It is often used for timeouts. Promise.any resolves on the first fulfilled promise and ignores earlier rejections.
function withTimeout(promise, ms) {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error(`Timed out after ${ms}ms`)), ms)
);
return Promise.race([promise, timeout]);
}
async function main() {
try {
const user = await withTimeout(fetchUser(1), 3000);
console.log(user.name);
} catch (err) {
console.error(err.message);
}
}In production
Promise.all with an unbounded array is parallelism by accident. Mapping over 500 IDs can exhaust a database pool or trigger rate limiting before the first response lands. Use bounded concurrency with a max in-flight count when the list can grow.