On this page

Promise.all, allSettled, race, any

Running Promises Together

When your code needs several async results, awaiting them one by one is slow. Each await waits for the previous one to finish.

The food delivery app's home screen needs three things:

1Sequential (await one after another)
2 getUser 100 ms
3 getRestaurants 150 ms
4 getOffers 80 ms
5 Total = 100 + 150 + 80 = 330 ms
6
7Parallel (start all, then wait)
8 getUser 100 ms
9 getRestaurants 150 ms
10 getOffers 80 ms
11 Total = slowest one = 150 ms

JavaScript has four built-in helpers for running promises together. They all take an array of promises but differ in when they finish and what they return:

MethodFinishes whenResultRejects when
Promise.allAll succeed, or the first one failsArray of valuesAny one fails (first failure)
Promise.allSettledAll finish, success or failureArray of { status, value / reason }Never
Promise.raceThe first one finishes, success or failureThat first resultThe first one to finish fails
Promise.anyThe first one succeedsThat first valueAll fail (AggregateError)

Think of ordering food for a group:

  • all: dinner starts only when every dish arrives. If one restaurant cancels, the plan fails.
  • allSettled: wait for every order, then see which arrived and which were cancelled.
  • race: whatever happens first, delivery or cancellation, decides.
  • any: order the same pizza from three places and eat the first one that arrives.

Promise.all: All or Nothing

Use it when you need every result and one failure makes the whole thing useless.

async function getHomeScreen(userId) {
const [user, restaurants, offers] = await Promise.all([
getUser(userId),
getRestaurants(userId),
getOffers(userId)
]);
return { user: user, restaurants: restaurants, offers: offers }; // values in input order
}
// if getOffers rejects, the whole await throws
try {
const screen = await getHomeScreen(42);
} catch (err) {
console.log('Home screen failed:', err.message);
}

Key points:

  • Results come back in the same order as the input array, not the order they finished.
  • It's fail-fast: it rejects as soon as one promise rejects, with that error.
  • The other promises are not cancelled. They keep running in the background; their results are just ignored.

Promise.allSettled: Wait for Everything

Use it when tasks are independent and you want every outcome, even if some fail. It never rejects.

After an order is placed, we notify the user three ways. One failing channel shouldn't hide the others:

async function notifyOrderPlaced(order) {
const results = await Promise.allSettled([
sendEmail(order.userId, order.id),
sendSms(order.userId, order.id),
sendPush(order.userId, order.id)
]);
const channels = ['email', 'sms', 'push'];
results.forEach(function (result, i) {
if (result.status === 'fulfilled') {
console.log(channels[i] + ' sent');
} else {
console.log(channels[i] + ' failed:', result.reason.message);
// e.g. push the failed channel to a retry queue
}
});
}
1[
2 { status: 'fulfilled', value: 'email-id-81' },
3 { status: 'rejected', reason: Error('SMS provider down') },
4 { status: 'fulfilled', value: 'push-id-12' }
5]

Promise.race: First to Finish Wins

race settles with the first promise to settle, whether it succeeded or failed. Its most common use is a timeout: race the real task against a timer that rejects.

function withTimeout(promise, ms) {
let timer;
const timeout = new Promise(function (_, reject) {
timer = setTimeout(function () {
reject(new Error('Timed out after ' + ms + ' ms'));
}, ms);
});
return Promise.race([promise, timeout]).finally(function () {
clearTimeout(timer); // don't leave the timer running
});
}
// payment gateway must answer within 3 seconds
try {
const payment = await withTimeout(chargeCard(order), 3000);
} catch (err) {
console.log(err.message); // 'Timed out after 3000 ms'
}

The timeout only stops you waiting. The slow chargeCard call keeps running and may still succeed later. For HTTP calls, cancel the request itself with an AbortSignal:

// actually cancels the request after 3 s (Node.js 17.3+)
const res = await fetch('https://payments.example.com/charge', {
method: 'POST',
body: JSON.stringify(order),
signal: AbortSignal.timeout(3000)
});

Promise.any: First Success Wins

any ignores failures and returns the first success. It rejects only if every promise fails, with an AggregateError that holds all the errors in err.errors.

We ask two map providers for a delivery-time estimate and use whichever answers successfully first:

async function getDeliveryEstimate(from, to) {
try {
return await Promise.any([
mapProviderA.estimate(from, to),
mapProviderB.estimate(from, to)
]);
} catch (err) {
// err is an AggregateError: every provider failed
err.errors.forEach(function (e) { console.log('Provider failed:', e.message); });
return { minutes: 40, approximate: true }; // fallback
}
}

race vs any: if provider A fails in 50 ms and provider B succeeds in 200 ms, race rejects with A's error, while any returns B's result.

Tricky Cases

CaseResult
Promise.all([])Resolves immediately with []
Promise.allSettled([])Resolves immediately with []
Promise.race([])Never settles, stays pending forever
Promise.any([])Rejects immediately with AggregateError
Non-promise values, like Promise.all([1, getUser()])Treated as already resolved: [1, user]
Promise.all with 10,000 database callsAll start at once, can exhaust the connection pool. Process in batches instead
// predict the output
const fast = new Promise(function (resolve) { setTimeout(function () { resolve('fast'); }, 100); });
const fail = new Promise(function (_, reject) { setTimeout(function () { reject(new Error('fail')); }, 50); });
const slow = new Promise(function (resolve) { setTimeout(function () { resolve('slow'); }, 200); });
Promise.all([fast, fail, slow]).catch(function (e) { console.log('all:', e.message); });
Promise.allSettled([fast, fail, slow]).then(function (r) { console.log('allSettled:', r.length); });
Promise.race([fast, fail, slow]).catch(function (e) { console.log('race:', e.message); });
Promise.any([fast, fail, slow]).then(function (v) { console.log('any:', v); });
// Output (by time):
// all: fail (at 50 ms)
// race: fail (at 50 ms)
// any: fast (at 100 ms)
// allSettled: 3 (at 200 ms)

Which One to Use

NeedUse
Every result is required (home screen data, order + payment + stock check)Promise.all
Independent tasks, report each outcome (notifications, bulk emails, cleanup jobs)Promise.allSettled
Timeout, or "whichever settles first"Promise.race
Several sources for the same thing, any success is fine (mirrors, fallback providers)Promise.any

Interview Angle

How to Explain it in an Interview

The 30-Second Spoken Pitch
"All four run promises concurrently but finish differently. Promise.all waits for all of them and returns values in input order, but rejects as soon as one fails, so I use it when every result is required, like loading a page's data. Promise.allSettled waits for everything and never rejects; each result has a status, so I use it for independent tasks like sending email, SMS, and push notifications. Promise.race settles with whichever promise finishes first, success or failure, which is handy for timeouts. Promise.any returns the first success and only fails with an AggregateError if all of them fail, which suits fallback providers. None of them cancel the remaining promises, so for real cancellation I use an AbortSignal, and for thousands of tasks I batch them instead of firing everything at once."

Common mistakes:

  • Awaiting independent calls one by one when they could run in parallel.
  • Thinking Promise.all cancels the other promises when one fails.
  • Mixing up race (first to settle) and any (first to succeed).
  • Using Promise.all over thousands of items and overloading the database or an external API.

Summary

1Sequential awaits → total = sum of times; parallel → total = slowest
2Promise.all → all succeed → values in order; first failure → reject
3Promise.allSettled → wait for all, never rejects, { status, value / reason }
4Promise.race → first to settle wins (success OR failure) → timeouts
5Promise.any → first success wins; all fail → AggregateError
6None cancel → use AbortSignal for real cancellation
7Many tasks → batch them, don't fire thousands at once

These helpers decide when promises finish. Next, the event loop explains how Node.js actually runs all these callbacks on a single thread.

Next TopicError Handling in Async Code