Promise Combinators
Advanced · 18 min read · ▶ live playground · ✦ checkpoint
You already know how to wait for one promise. Real apps rarely stop there. A scoreboard loads three players, a checkout checks price and stock, a profile tries backup sources. Promise combinators are promise methods that take several promises and return one new promise describing the group result.
Think of the group promise as one front desk collecting several pickup receipts. You choose the rule: "all must succeed," "tell me everything that happened," "first one wins," or "first success wins."
Promise.all: Parallel and Fail-Fast
Promise.all(promises) starts with promises that are already in motion and gives you one promise for the whole group. It fulfills with an array of values when every input fulfills. The array keeps the input order, not the finish order.
function loadScore(player, delay, shouldFail = false) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (shouldFail) {
reject(new Error(`${player} score missing`));
return;
}
resolve({ player, score: delay + 10 });
}, delay);
});
}
async function printTeamScores() {
const scores = await Promise.all([
loadScore("Mina", 30),
loadScore("Rae", 10),
loadScore("Nico", 20),
]);
for (const record of scores) {
console.log(`${record.player}: ${record.score}`);
}
}
async function printBrokenTeam() {
try {
await Promise.all([
loadScore("Mina", 30),
loadScore("Rae", 10, true),
loadScore("Nico", 20),
]);
} catch (error) {
console.log(`team failed: ${error.message}`);
}
}
async function run() {
await printTeamScores();
await printBrokenTeam();
}
run();→ Mina: 40, Rae: 20, Nico: 30, then team failed: Rae score missing.
Fail-fast means Promise.all rejects as soon as one input rejects, instead of waiting for every result. That is right when one missing piece makes the whole operation unusable: "show the team only if every score loaded."
allSettled, race, and any
Promise.allSettled(promises) waits for every input to settle. Use it when failure is data you still want to report, not a reason to stop.
function saveMood(player, mood, shouldSave, delay) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (!shouldSave) {
reject(new Error(`${player} mood rejected`));
return;
}
resolve(`${player}: ${mood}`);
}, delay);
});
}
function checkScoreSource(source, shouldWork, delay) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (!shouldWork) {
reject(new Error(`${source} failed`));
return;
}
resolve(`${source} has the score`);
}, delay);
});
}
async function printMoodReport() {
const results = await Promise.allSettled([
saveMood("Mina", "focused", true, 20),
saveMood("Rae", "tired", false, 10),
saveMood("Nico", "ready", true, 30),
]);
for (const result of results) {
if (result.status === "fulfilled") {
console.log(`saved: ${result.value}`);
} else {
console.log(`missed: ${result.reason.message}`);
}
}
}
async function printRaceResult() {
try {
const message = await Promise.race([
checkScoreSource("slow source", true, 30),
checkScoreSource("broken source", false, 10),
checkScoreSource("backup source", true, 20),
]);
console.log(`race: ${message}`);
} catch (error) {
console.log(`race stopped: ${error.message}`);
}
}
async function printAnyResult() {
const message = await Promise.any([
checkScoreSource("cache source", false, 10),
checkScoreSource("backup source", true, 30),
checkScoreSource("local source", true, 20),
]);
console.log(`any: ${message}`);
}
async function run() {
await printMoodReport();
await printRaceResult();
await printAnyResult();
}
run();→ saved: Mina: focused, missed: Rae mood rejected, saved: Nico: ready, race stopped: broken source failed, then any: local source has the score.
Promise.race(promises) settles with the first promise to settle, whether that first result is success or failure. Use it when the first signal matters: a timeout promise against a real operation is the classic shape, with cancellation details coming in 12.5.
Promise.any(promises) fulfills with the first successful promise and ignores earlier rejections. Use it when you have backups and only need one winner. If every input rejects, any rejects with an AggregateError, an error object that groups several rejection reasons.
Try it — choose the group rule
Run this and compare the three group rules against the same kind of work. The delays are distinct, so the order is deterministic.
all is strict. allSettled is a full report. any is optimistic: give it several chances, and it takes the first success.
Modern Helpers: withResolvers and try
Promise.withResolvers() creates a promise plus its matching resolve and reject functions. Use it when something outside the executor must decide the result later, such as a small event bridge or test harness.
function createScoreGate(player) {
const { promise, resolve, reject } = Promise.withResolvers();
return {
promise,
approve(score) {
resolve({ player, score });
},
rejectScore(reason) {
reject(new Error(reason));
},
};
}
const gate = createScoreGate("Mina");
gate.promise
.then(record => {
console.log(`${record.player}: ${record.score}`);
})
.catch(error => {
console.log(`gate failed: ${error.message}`);
});
setTimeout(() => {
gate.approve(48);
}, 10);→ Mina: 48.
Promise.try(fn) starts by calling fn and returns a promise. If fn returns a value, the promise fulfills with it. If fn throws, the promise rejects. If fn returns a promise, Promise.try follows that promise.
function readMood(player) {
if (player === "") {
throw new Error("Player is required");
}
return `${player}: focused`;
}
Promise.try(() => readMood("Mina"))
.then(message => {
console.log(message);
})
.catch(error => {
console.log(`failed: ${error.message}`);
});Reach for Promise.try when one step might throw synchronously today or return a promise tomorrow, and you want one promise-shaped path either way.
You can now combine future results instead of awaiting them one by one. Next, you learn the timer and cancellation-shaped patterns that keep async work polite: correct intervals, timeout helpers, debounce, throttle, and async iteration.
Checkpoint
Answer all three to mark this lesson complete