Promise Combinators
Promise combinators are static methods — Promise.all , allSettled , race , and any — that take an array of promises and combine them into a single promise, letting you coordinate many async operations at once.
Learn Promise Combinators in our free JavaScript course — an interactive lesson with runnable examples, a practice exercise and a quick reference.
Part of the free JavaScript course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
They are the tools you reach for whenever you have several async jobs running together — fetching from five APIs, racing a request against a timeout, or collecting results even when some calls fail.
📚 Prerequisites: You should be comfortable with promises and async/await . Combinators build directly on those ideas.
🍽️ Real-World Analogy: Imagine ordering food for a table:
Promise.all takes an iterable of promises and returns one promise that resolves to an array of all their values — in the same order you passed them , regardless of which finished first. It is fail-fast : if any promise rejects, the combined promise rejects immediately with that error and the other results are discarded.
Because all the promises are already running, the total time is roughly the time of the slowest one — not the sum. That is the parallel speed-up you came for.
Here is the real-world fan-out pattern with fetch :
⚠️ Heads up: One rejection loses every result. If you need the successful ones even when some fail, reach for Promise.allSettled instead.
Promise.allSettled waits for every promise to finish and never rejects. Instead of values, you get an array of result objects: {' '} for successes and {' '} for failures. It is perfect when one slow or broken source should not sink the whole operation.
Use Promise.all to await both promises at once and log the array.
Both settle as soon as a single promise settles, but they disagree about failures. Promise.race takes whichever promise settles first — even if it rejects — which makes it the classic way to bolt a timeout onto slow work. Promise.any ignores rejections and resolves with the first fulfillment ; only if every promise rejects does it reject, with an AggregateError whose .errors array holds every reason.
💡 Tip: Use race for timeouts (a fast rejection should win) and any for redundancy (you only need one source to succeed).
Use Promise.race so the slow work loses to the 30ms timeout.
Predict the output before revealing the answer.
It rejects with "x" . Promise.all is fail-fast — the 1 and 3 never reach you.
"rejected" — allSettled never throws; it reports the failure as a result object.
It rejects with an AggregateError because every promise rejected; .errors is ["a", "b"] .
Load three "widgets" in parallel. Two succeed, one fails. Use Promise.allSettled so the dashboard still shows the widgets that loaded and counts the failures.
Up next: Async Iterators & for-await-of — streaming data one chunk at a time. 🔁
Practice quiz
What does Promise.all resolve to when every promise fulfills?
- The first value only
- An array sorted by finish time
- An array of all values in input order
- A single combined object
Answer: An array of all values in input order. Promise.all resolves to an array of all values, in the same order you passed them in.
What happens to Promise.all if one input promise rejects?
- It rejects immediately and discards other results (fail-fast)
- It ignores the failure
- It waits for the rest then rejects
- It resolves with the successes
Answer: It rejects immediately and discards other results (fail-fast). Promise.all is fail-fast: one rejection rejects the whole thing and the other results are lost.
How does Promise.allSettled differ from Promise.all?
- It rejects faster
- It only takes two promises
- It runs them sequentially
- It never rejects and reports every outcome
Answer: It never rejects and reports every outcome. allSettled waits for every promise and gives an array of { status, value/reason } — it never rejects.
What shape does each Promise.allSettled result object have for a success?
- { ok: true, data }
- { status: 'fulfilled', value }
- { resolved: value }
- Just the value
Answer: { status: 'fulfilled', value }. Fulfilled entries are { status: 'fulfilled', value }; failures are { status: 'rejected', reason }.
Which combinator is the classic choice for adding a timeout to slow work?
- Promise.race
- Promise.all
- Promise.allSettled
- Promise.any
Answer: Promise.race. Promise.race settles with the first promise to settle (even a rejection), so a timeout rejection can win.
How does Promise.any treat rejections?
- It rejects on the first rejection
- It collects them into an array of values
- It ignores rejections and resolves with the first fulfillment
- It retries them
Answer: It ignores rejections and resolves with the first fulfillment. Promise.any resolves with the first fulfillment, ignoring rejections unless they all reject.
What does Promise.any reject with when every input promise rejects?
- The last error
- An AggregateError holding all the reasons
- undefined
- A TypeError
Answer: An AggregateError holding all the reasons. If all reject, Promise.any rejects with an AggregateError whose .errors array holds every reason.
Do the combinators start your promises running?
- Yes, they trigger the work
- Only Promise.all does
- Only with await
- No — promises run when created; the combinators only wait
Answer: No — promises run when created; the combinators only wait. Promises start the moment they are created; combinators merely coordinate when to wait on them.
What is the result of await Promise.all([1, Promise.reject('x'), 3])?
- x
Promise.all is fail-fast, so the rejection 'x' rejects the whole call and 1 and 3 are discarded.
Which mistake turns parallel work into accidental sequential work?
- Using Promise.all(urls.map(fetch))
- Passing already-running promises
- Awaiting each fetch inside a for loop
- Using allSettled
Answer: Awaiting each fetch inside a for loop. Awaiting one fetch before starting the next serializes them; map them into Promise.all to overlap.