
Promise.all() vs. Promise.race() in JavaScript

Once developers move past the first "what is a promise?" stage, the next source of confusion is usually not promise syntax. It is promise coordination.
One async task is easy enough to picture. Three async tasks at once is where the behaviour starts mattering:
- should we wait for all of them
- should we continue as soon as one settles
- what happens if one fails
That is where Promise.all() and Promise.race() come in.
They are often mentioned together because both accept multiple promises, but they solve very different problems.
Promise.all() waits for everything to succeed
At its simplest:
const results = await Promise.all([ fetchUser(), fetchOrders(), fetchRecommendations(),]);Promise.all() waits until every promise in the array has fulfilled.
When that happens, it resolves with an array of results in the same order as the input promises.
That order point matters. Even if one request finishes first in time, the results still come back in the array positions you provided originally.
If one promise rejects, the whole Promise.all() rejects
This is the behaviour that most often surprises beginners.
If any one of the promises rejects, Promise.all() rejects immediately with that error.
So if we do this:
await Promise.all([ fetchUser(), fetchOrders(), fetchRecommendations(),]);and fetchOrders() fails, the combined promise rejects even if the others succeeded.
That makes sense when the results are all required together. It is less helpful when partial success would still be useful.
Promise.all() is ideal when the tasks belong together
Good use cases include:
- loading several pieces of page data in parallel
- fetching related resources before rendering
- performing independent async work where every result is required
For example:
const loadDashboard = async (): Promise<void> => { try { const [user, orders, recommendations] = await Promise.all([ fetchUser(), fetchOrders(), fetchRecommendations(), ]); console.log(user, orders, recommendations); } catch (error) { console.error('Dashboard data failed to load', error); }};This is much faster than awaiting each request one after another if they do not depend on each other.
Promise.race() is not about collecting everything
Promise.race() does something quite different.
It settles as soon as the first promise in the group settles, whether that first outcome is a fulfilment or a rejection.
const result = await Promise.race([ fetchPrimaryData(), fetchFallbackData(),]);If fetchFallbackData() resolves first, that becomes the result. If fetchPrimaryData() rejects first, the race rejects immediately.
This is not "whichever succeeds first". It is "whichever settles first".
That Distinction Matters a Lot
People often talk about Promise.race() as if it picks the first successful promise. It does not.
If the fastest promise fails, the race fails.
That makes it useful for timeout patterns, but also easy to misuse if you expected it to quietly ignore early rejections.
A Timeout Example is the Classic Use Case
Suppose we want to give a request five seconds before treating it as timed out:
const timeout = (ms: number): Promise<never> => new Promise((_, reject) => { window.setTimeout(() => { reject(new Error('Request timed out')); }, ms); });const loadProducts = async (): Promise<Response> => { return Promise.race([ fetch('/api/products'), timeout(5000), ]);};If fetch() finishes first, we get its response. If the timeout promise rejects first, the race rejects.
That is a practical pattern because the fastest settlement is exactly what we care about.
Promise.race() does not cancel the losers
This is another important caveat.
If one promise wins the race, the others do not magically stop existing.
That means a timed‑out fetch may still continue in the background unless we explicitly abort it using something like AbortController.
So Promise.race() is about choosing the first settled result, not cancelling the rest of the work.
A common performance mistake with Promise.all()
Another beginner trap is this pattern:
const user = await fetchUser();const orders = await fetchOrders();const recommendations = await fetchRecommendations();That runs the operations one after another.
If they are independent, Promise.all() is often better:
const [user, orders, recommendations] = await Promise.all([ fetchUser(), fetchOrders(), fetchRecommendations(),]);The improvement is not that promises become "more async". It is that the work is started in parallel rather than serially.
Choose the Method Based on the Question You are Asking
This is the simplest decision rule.
Use Promise.all() when the question is:
"When all of these tasks have completed successfully, give me all the results."
Use Promise.race() when the question is:
"Whichever settles first, I want that outcome."
Once you phrase the problem properly, the choice is usually obvious.
Error Handling Follows the Method's Purpose
With Promise.all(), one rejection means the group has failed.
With Promise.race(), the first rejection wins just as surely as the first fulfilment would.
That means the right error strategy depends on whether failure in one member should collapse the whole operation or whether a timeout‑style first‑settled rule is the real goal.
These Methods are Powerful Because They Describe Intent
Good async code is not only about syntax like then() or await. It is about expressing the relationship between tasks clearly.
Promise.all() says:
- these tasks belong together
- I need all the results
Promise.race() says:
- the earliest settled outcome decides what happens next
That is a meaningful difference in program behaviour, not just a minor API variation.
Once You See the Difference, the Names Make Sense
Promise.all() is collaborative.
Promise.race() is competitive.
That is not a perfect formal definition, but it is a good practical memory aid. One waits for the whole group. The other stops caring once the first result arrives.
When you keep that picture in mind, both APIs become much easier to use correctly.
Related Articles

Generators in JavaScript: A Beginner's Guide. 
Tips for Managing Memory in JavaScript. Tips for Managing Memory in JavaScript

Why We Use an Empty Dependency Array in React's useEffect Hook. Why We Use an Empty Dependency Array in React's
useEffectHook
Building Polyfills for JavaScript Array and String Methods. Building Polyfills for JavaScript Array and String Methods

Using next/link for Client‑Side Navigation. Using
next/linkfor Client‑Side Navigation
Prefix Sums and Hash Maps: Solving 'Subarray Sum Equals K'. Prefix Sums and Hash Maps: Solving 'Subarray Sum Equals K'

Comparing Arrays in JavaScript. Comparing Arrays in JavaScript

Breadth‑First Search: Solving Binary Tree Level Order Traversal. Breadth‑First Search: Solving Binary Tree Level Order Traversal

Life as a Freelance Developer in Brighton. Life as a Freelance Developer in Brighton

Object Equality in JavaScript: {} isn't Equal to {}. Object Equality in JavaScript:
{}isn't Equal to{}
What Makes a Great JavaScript Developer? What Makes a Great JavaScript Developer?

The Longest Palindromic Substring in JavaScript. The Longest Palindromic Substring in JavaScript