Quiz

How is `Promise.all()` different from `Promise.allSettled()`?

Topics
AsyncJavaScript

TL;DR

Promise.all() and Promise.allSettled() are both methods for handling multiple promises in JavaScript, but they behave differently. Promise.all() waits for all promises to resolve and fails fast if any promise rejects, returning a single rejected promise. Promise.allSettled(), on the other hand, waits for all promises to settle (either resolve or reject) and returns an array of objects describing the outcome of each promise.


How is Promise.all() different from Promise.allSettled()?

Promise.all()

Promise.all() takes an iterable of promises and returns a single promise that resolves when all of the input promises have resolved. If any of the input promises reject, the returned promise immediately rejects with the reason of the first promise that rejected.

Example

const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3])
.then((values) => {
console.log(values); // [3, 42, 'foo']
})
.catch((error) => {
console.error(error);
});

In this example, Promise.all() resolves with an array of results [3, 42, 'foo'] because all promises resolve successfully.

Failure case

const promise1 = Promise.resolve(3);
const promise2 = Promise.reject('error');
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3])
.then((values) => {
console.log(values);
})
.catch((error) => {
console.error(error); // 'error'
});

In this example, Promise.all() rejects immediately with the reason 'error' because promise2 rejects.

Promise.allSettled()

Promise.allSettled() takes an iterable of promises and returns a single promise that resolves when all of the input promises have settled (either resolved or rejected). The returned promise resolves with an array of objects that each describe the outcome of each promise.

Example

const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.allSettled([promise1, promise2, promise3]).then((results) => {
results.forEach((result) => console.log(result));
// { status: 'fulfilled', value: 3 }
// { status: 'fulfilled', value: 42 }
// { status: 'fulfilled', value: 'foo' }
});

In this example, Promise.allSettled() resolves with an array of result objects, each indicating the status and value of the promises.

Failure case

const promise1 = Promise.resolve(3);
const promise2 = Promise.reject('error');
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.allSettled([promise1, promise2, promise3]).then((results) => {
results.forEach((result) => console.log(result));
// { status: 'fulfilled', value: 3 }
// { status: 'rejected', reason: 'error' }
// { status: 'fulfilled', value: 'foo' }
});

In this example, Promise.allSettled() resolves with an array of result objects, including the rejected promise with its reason.

When to use Promise.all() vs Promise.allSettled()

The choice comes down to a single question: if one of these operations fails, is the rest of the work still useful?

Use Promise.all() when partial success is invalid

  • Prefetching everything a page needs before render: if any required call fails (auth, feature flags, current user), you want to render an error state, not a half-broken UI.
  • Transactional sequences: booking a flight, hotel, and rental car in parallel. If the flight fails, the other reservations should not silently succeed.
  • Gating UI on multiple checks: show the dashboard only if getUser() AND getPermissions() both succeed. Fail-fast is the desired behavior.

Use Promise.allSettled() when partial success is acceptable

  • Dashboard with multiple independent widgets: six analytics endpoints fan out in parallel. One slow or failing endpoint should not blank the rest of the dashboard.
  • Bulk operations with per-item feedback: deleting 50 selected rows; the UI needs to show which succeeded and which failed, not abort on the first failure.
  • Analytics or telemetry batches: sending several events where partial loss is acceptable; you want to know which fired even if one rejected.

If you would try/catch each call individually anyway, Promise.allSettled() is what you actually want.

Failure-mode reference

Input scenarioPromise.all()Promise.allSettled()
Empty iterable []Resolves with [] synchronouslyResolves with [] synchronously
Mix of promises and non-promise valuesWraps non-promises with Promise.resolveWraps non-promises with Promise.resolve
One rejection, others pendingRejects immediately with first reason; others keep running but their results are ignoredWaits for all; returns one {status:'rejected'} entry
Synchronous throw inside the iterable iteratorReturned promise rejectsReturned promise rejects
Non-iterable input (e.g., undefined)Returned promise rejects with TypeErrorReturned promise rejects with TypeError
Result order vs resolution orderResults array preserves input order, not the order they resolvedResults array preserves input order

Predict the output

Concurrent vs sequential awaits

These two snippets look almost identical but run very differently:

const slow = (label, ms) =>
new Promise((resolve) => setTimeout(() => resolve(label), ms));
(async () => {
console.time('parallel');
const a = await Promise.all([slow('A', 300), slow('B', 300)]);
console.timeEnd('parallel'); // ~300ms: both ran concurrently
console.time('sequential');
const b = [await slow('A', 300), await slow('B', 300)];
console.timeEnd('sequential'); // ~600ms: second await waits for the first
})();

Promise.all() accepts already-running promises and waits for the slowest. [await ..., await ...] evaluates left-to-right and only kicks off the next call after the previous one resolves. The latter is one of the most common production performance bugs.

Promise.allSettled() does not reject on per-promise failures

(async () => {
const results = await Promise.allSettled([
Promise.resolve(1),
Promise.reject(new Error('boom')),
Promise.resolve(3),
]);
console.log(results.map((r) => r.status));
// ['fulfilled', 'rejected', 'fulfilled']
// The outer await never throws for inner rejections, so this line still runs:
console.log('still here');
})();

You will almost never put Promise.allSettled() inside a try/catch for the inner promises, since it does not throw on their rejections. To check for failures, filter on status === 'rejected' after the fact. (The returned promise can still reject if the input itself is invalid — for example, a non-iterable will produce a TypeError.)

Further reading

Edit on GitHub