Asyncadvanced
Promise.all, allSettled, race, and any
Four static Promise combinators for running multiple promises concurrently, each with different rules for how they settle.
Four ways to combine promises, differing in when they settle and what they do with failures.
Promise.all is a relay race where the team's time only counts if everyone finishes. allSettled records every runner's result regardless. race is whoever crosses first. any only cares about first success, failing only if everyone trips.
Key Concepts
1
Promise.all all fulfil -> array of values
any rejects -> rejects immediately
Promise.allSettled waits for all -> array of {status, value/reason}
never rejects
Promise.race the first to SETTLE wins, fulfilled or rejected
Promise.any the first to FULFIL wins
all reject -> AggregateError2
all is the common one, and its failure behaviour is the thing to know.
const [a, b, c] = await Promise.all([fa(), fb(), fc()]);all
3
One rejection rejects the whole thing, and you lose the results of the others — they still completed, and
you cannot reach them.
4
So all is right when you need every result and a failure makes the operation pointless.
all
5
allSettled is right when partial success is useful.
const results = await Promise.allSettled(userIds.map(fetchUser));
const ok = results.filter(r => r.status === 'fulfilled')
.map(r => r.value);
const failed = results.filter(r => r.status === 'rejected');allSettled
6
Loading ten dashboard widgets: one failing should not blank the page. all would.
all
7
race for timeouts.
await Promise.race([
fetch(url),
new Promise((_, rej) => setTimeout(() => rej(new Error('timeout')), 5000))
]);race
8
Whichever settles first wins. Note the losing promise is not cancelled — the fetch continues, and its result
is discarded. AbortController is what actually cancels it.
AbortController
9
any for redundancy. Query three mirrors and take the first that succeeds; failures are ignored unless
all of them fail.
any
10
All four start everything immediately. They take promises, and a promise is already running.
11
// all three requests start here, before Promise.all is called
const ps = [fa(), fb(), fc()];
await Promise.all(ps);12
Which is why ids.map(id => fetch(id)) launches every request at once. To limit concurrency you need a
pool — a library, or batching the array yourself.
ids.map(id => fetch(id))
13
One practical caution with all on a large array. A thousand simultaneous requests will hit rate limits
or exhaust connections. Chunk the input rather than mapping the whole array.
all
14
What the interviewer is probing.1. "What happens to the others when Promise.all rejects?" Probing: the fail-fast behaviour.
Stalls: "They are cancelled." Moves up: it rejects immediately on the first failure, but the
others keep running — promises cannot be cancelled.
15
2. "When do you want allSettled?" Probing: the difference. Stalls: "Never." Moves up: when
you need every result regardless of individual failures, such as fanning out to several services and
reporting per-service status.
16
3. "race versus any?" Probing: the two. Stalls: "The same." Moves up: race settles with
the first to settle including a rejection; any resolves with the first success and only rejects if
all fail.
17
4. "How do you add a timeout to a fetch?" Probing: the practical use. Stalls: "Use
setTimeout." Moves up: race the fetch against a promise that rejects on a timer, or better, use
AbortController so the request is actually aborted.