skip to content

Promise.all vs Promise.allSettled

all fails fast on the first rejection and discards the other results; allSettled always waits and hands you a status per entry. Interviewers probe the detail that a rejected all does not cancel the work still in flight.

part ofJavaScriptoverview, primer and where to startread it →
on this pageshow

questions

5

What is the difference between Promise.all and Promise.allSettled in JavaScript, and what does each of them fulfil with?

level: juniorimportance: must knowfreq 78%

answer

  1. one aggregate promise, two failure policies
  2. fail-fast versus wait-for-everyone
  3. status descriptors instead of bare values
  4. first rejection reason, other results discarded
  5. ES2020 addition that never rejects

basics

~20 s

Promise.all rejects the moment any input promise rejects, and otherwise fulfils with an array of the values in input order. Promise.allSettled waits for every input and always fulfils, giving one {status, value or reason} object per entry.

solid answer

~40 s

Both take an iterable of promises and return a single aggregate promise, but they disagree on what counts as done. `Promise.all` is all-or-nothing: it fulfils with an array of values, positionally matched to the input, only if every input fulfils; if any input rejects it rejects immediately with that first rejection reason and discards the other results. `Promise.allSettled`, added in ES2020, never rejects — it waits for every input to settle either way and fulfils with an array of descriptor objects, `{ status: 'fulfilled', value }` for successes and `{ status: 'rejected', reason }` for failures. So I reach for `all` when the operation is meaningless without every piece and I want the error to propagate, and for `allSettled` when partial results are useful and I need to see which entries failed and why.

go deeper

for a junior

Be able to state the two behaviours plainly: one rejects on the first failure and gives you an array of values, the other always succeeds and gives you a status object per entry. Naming the {status, value, reason} shape from memory is expected.

for a middle

Explain the mechanics behind the difference — positional result ordering, fail-fast rejecting with a single reason, and the fact that on the happy path both settle at the same time. Be ready to write the filter-by-status code that splits successes from failures.

for a senior

Show judgment about which failure policy the caller actually needs, and point out that a fail-fast rejection leaves the sibling work running with its side effects intact. Interviewers expect you to raise the diagnostic loss when only the first reason survives.

for a principal

Own the API-contract angle: an all-or-nothing endpoint and a partial-results endpoint are different contracts, and switching to allSettled means the response must carry per-section status plus logging for the swallowed reasons. Decide it from the invariants the response must uphold, not from taste.

## The shared shape `Promise.all` and `Promise.allSettled` are both static methods on the `Promise` constructor. Each takes an **iterable** — normally an array, but any iterable works — walks it, passes every entry through `Promise.resolve` so plain values and thenables are treated uniformly, and returns one new promise that aggregates the whole batch. Neither of them *starts* anything: by the time you build the array, the functions inside it have already been called and the underlying work is already in flight. The combinator is only a way to observe when the batch is done. They differ in exactly one decision: what counts as "done", and therefore what the aggregate promise settles with. ## Promise.all — all-or-nothing `Promise.all` fulfils only when **every** input has fulfilled. Its fulfilment value is an array of the individual values, in the **order of the input iterable**, regardless of which finished first. ```js const [user, orders] = await Promise.all([fetchUser(id), fetchOrders(id)]); ``` If any input rejects, the aggregate rejects **immediately** with that single rejection reason — not an array of reasons, not a partial result. This is called fail-fast. The `await` above throws exactly as if a single call had thrown, which is why `Promise.all` composes so naturally with `try/catch`: the failure of any part is the failure of the whole. Two consequences follow that candidates often miss. First, the values of the inputs that did succeed are gone — the aggregate promise settles once, and it settled as a rejection. Second, rejecting the aggregate does not stop the other inputs: a promise is a handle on work that is already running, not a job you can call off, so the remaining requests still complete and their side effects still land. ## Promise.allSettled — a report per entry `Promise.allSettled` (ES2020) fulfils once every input has **settled**, fulfilled or rejected. It essentially never rejects; you always get an array back, again positionally matched to the input. Each element is a descriptor object: ```js const results = await Promise.allSettled([a(), b(), c()]); // [{ status: 'fulfilled', value: 1 }, // { status: 'rejected', reason: Error }, // { status: 'fulfilled', value: 3 }] ``` A fulfilled descriptor has `status` and `value`; a rejected one has `status` and `reason`. The other property is simply absent, so reading `.value` on a rejected entry gives `undefined` rather than throwing. Because the aggregate always fulfils, **you** are responsible for inspecting statuses — nothing throws on your behalf: ```js const ok = results.filter(r => r.status === 'fulfilled').map(r => r.value); const failures = results.filter(r => r.status === 'rejected').map(r => r.reason); ``` ## Timing: which one settles sooner A common assumption is that `allSettled` is "the slow one". On the happy path both settle at the same moment — when the slowest input finishes. They diverge only when something rejects: `all` can settle much earlier (the instant of the first rejection), while `allSettled` still waits for every straggler. If you need every operation to be finished before you continue — cleanup, a summary log, a transaction boundary — `allSettled` is the one that actually guarantees it. ## Choosing between them Ask whether the caller's result is meaningful without one of the pieces. A page that needs the user record *and* their permissions is all-or-nothing: use `all` and let the rejection propagate. A dashboard with five independent widgets, or a batch importer processing 200 rows, is not: use `allSettled`, render what arrived, and report the rest. ## Edge cases worth knowing - Both fulfil immediately with `[]` for an empty iterable. - Non-promise entries pass straight through: `Promise.all([1, Promise.resolve(2)])` fulfils with `[1, 2]`. - `Promise.all` rejects with the *first* reason only; later rejections are dropped. They do not surface as unhandled rejections, because `all` attached handlers to every input up front — but the diagnostic detail in them is lost unless you capture it yourself. - Neither offers cancellation, and neither retries; those are separate concerns you layer on top.

  • If two of the inputs to Promise.all reject, what happens to the second rejection?
    The aggregate promise has already settled with the first reason, so the second is simply ignored — a promise settles once. It does not become an unhandled rejection either, because `Promise.all` attached a handler to every input when it was called. The practical cost is diagnostic: you never see that second error unless you wrap each input in its own `.catch` or use `Promise.allSettled` instead.
  • Does Promise.allSettled ever reject?
    For practical purposes no — every input settling either way still fulfils the aggregate. The only realistic way to get a rejection is a malformed argument: passing a non-iterable, or an iterator whose `next` throws, rejects the returned promise with that error. That is a programmer mistake, not a task failure, so you still cannot rely on `try/catch` to reveal that one of your operations failed.
  • What does Promise.all fulfil with if you pass it an empty array?
    It fulfils right away with an empty array. `Promise.allSettled([])` behaves the same way. This matters when the batch size is computed at runtime: an empty result is not a signal that something went wrong, so code that treats `results.length === 0` as an error will misfire on the legitimate no-work case.

saying these in an interview costs you the question

  • Claims Promise.all rejects with an array of all errors
  • Says Promise.allSettled rejects when any entry fails
  • Thinks Promise.all returns results in completion order
  • Believes a rejected Promise.all cancels the remaining work
  • Assumes allSettled entries are the plain resolved values

context

open as a page

Three uploads are running under a single Promise.all and the second one rejects after 100 ms, so the await throws right away. What is happening to the other two uploads, and what problems does that create?

level: seniorimportance: must knowfreq 55%

basics

~20 s

They keep running to completion. Promise.all's fail-fast rejection only settles the aggregate promise; it has no power to stop work already in flight, so the other uploads still finish, still write their side effects, and their outcomes are silently discarded.

open as a page

Promise.allSettled never rejects. Given that, what must your code do with the array it fulfils with, and what bug appears if you treat those entries as if they were plain resolved values?

level: middleimportance: should knowfreq 52%

basics

~20 s

You must inspect each entry's status yourself: fulfilled entries carry value, rejected ones carry reason and no value at all. Treating the array as plain values yields undefined for every failure, and a try/catch around the await never fires, so failures pass silently.

open as a page

When Promise.all fulfils, in what order do the values appear in the resulting array, and what happens to non-promise entries in the iterable you pass it?

level: middleimportance: should knowfreq 58%

basics

~20 s

Promise.all fulfils with values in the order of the input iterable, never in completion order, so index 0 always holds the first input's value. Non-promise entries are passed through Promise.resolve and appear unchanged at their own positions.

open as a page

A dashboard endpoint fans out to five independent services with Promise.all, and one flaky service takes the whole page down. How do you decide whether to keep all-or-nothing or move to Promise.allSettled, and what has to change if you switch?

level: principalimportance: should knowfreq 38%

basics

~20 s

Decide from the invariant: keep Promise.all when the response is meaningless or misleading without every part, and move to Promise.allSettled when sections are genuinely independent. Switching changes the response contract, so the payload must mark which sections are degraded and the reasons must be logged.

open as a page