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 ms3 getRestaurants 150 ms4 getOffers 80 ms5 Total = 100 + 150 + 80 = 330 ms6 7Parallel (start all, then wait)8 getUser 100 ms9 getRestaurants 150 ms10 getOffers 80 ms11 Total = slowest one = 150 msJavaScript 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:
| Method | Finishes when | Result | Rejects when |
|---|---|---|---|
Promise.all | All succeed, or the first one fails | Array of values | Any one fails (first failure) |
Promise.allSettled | All finish, success or failure | Array of { status, value / reason } | Never |
Promise.race | The first one finishes, success or failure | That first result | The first one to finish fails |
Promise.any | The first one succeeds | That first value | All 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 throwstry {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 secondstry {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 failederr.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
| Case | Result |
|---|---|
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 calls | All start at once, can exhaust the connection pool. Process in batches instead |
// predict the outputconst 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
| Need | Use |
|---|---|
| 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
Common mistakes:
- Awaiting independent calls one by one when they could run in parallel.
- Thinking
Promise.allcancels the other promises when one fails. - Mixing up
race(first to settle) andany(first to succeed). - Using
Promise.allover thousands of items and overloading the database or an external API.
Summary
1Sequential awaits → total = sum of times; parallel → total = slowest2Promise.all → all succeed → values in order; first failure → reject3Promise.allSettled → wait for all, never rejects, { status, value / reason }4Promise.race → first to settle wins (success OR failure) → timeouts5Promise.any → first success wins; all fail → AggregateError6None cancel → use AbortSignal for real cancellation7Many tasks → batch them, don't fire thousands at onceThese helpers decide when promises finish. Next, the event loop explains how Node.js actually runs all these callbacks on a single thread.