# Promise.all vs allSettled vs race vs any

> Learn when to use Promise.all, allSettled, race and any in JavaScript, how each handles rejections, and the common mistakes that bite in production.

Author: [Flavio Copes](https://flaviocopes.com/about/) | Published: 2026-07-29 | Topics: [JavaScript](https://flaviocopes.com/tags/js/) | Canonical: https://flaviocopes.com/promise-all-allsettled-race-any/

JavaScript gives us 4 ways to combine multiple promises: `Promise.all()`, `Promise.allSettled()`, `Promise.race()` and `Promise.any()`.

They look similar. They behave very differently when something fails.

I wrote about [promises](https://flaviocopes.com/javascript-promises/) and [waiting for multiple promises](https://flaviocopes.com/how-to-wait-multiple-promises-javascript/) before. This post is about picking the right combinator, because picking the wrong one is a classic source of subtle bugs.

Here's the short version:

- `Promise.all()` — wait for everything, fail fast on the first rejection
- `Promise.allSettled()` — wait for everything, never fail
- `Promise.race()` — take the first one that settles, win or lose
- `Promise.any()` — take the first one that fulfills, ignore failures

Let's go through each one.

## Promise.all: everything must succeed

Use `Promise.all()` when you need every result, and one failure should abort the whole operation.

The classic case is loading data where each piece is required:

```js
const [users, posts] = await Promise.all([
  fetchUsers(),
  fetchPosts(),
])
```

If both fulfill, you get an array of results in the same order you passed the promises in. Not the order they finished in.

If any promise rejects, `Promise.all()` rejects immediately with that error. This is called **failing fast**.

There's a detail people miss: the other promises keep running. `Promise.all()` doesn't cancel anything, because promises can't be cancelled. It just stops waiting. If `fetchUsers()` fails after 100ms, your `await` throws at 100ms, but `fetchPosts()` still completes in the background. Its result is thrown away.

I covered the basics of this pattern in [how to wait for all promises to resolve](https://flaviocopes.com/javascript-resolve-all-promises/), so here I'll just add my advice: use `Promise.all()` as your default. Most of the time, if one required thing fails, you can't do useful work anyway.

## Promise.allSettled: collect every outcome

Sometimes partial success is fine. You're sending 10 notification emails, and you don't want one bad address to hide the 9 that worked.

`Promise.allSettled()` waits for every promise to settle, and never rejects:

```js
const results = await Promise.allSettled([
  sendEmail('flavio@example.com'),
  sendEmail('bad-address'),
])
```

Each result is an object with a `status` field:

```js
[
  { status: 'fulfilled', value: undefined },
  { status: 'rejected', reason: Error }
]
```

You have to inspect each one:

```js
for (const result of results) {
  if (result.status === 'fulfilled') {
    console.log('sent:', result.value)
  } else {
    console.log('failed:', result.reason)
  }
}
```

Notice that fulfilled results have a `value` and rejected ones have a `reason`. Mixing those up is a common typo that TypeScript catches and plain JavaScript silently doesn't.

## Promise.race: first to settle wins

`Promise.race()` settles as soon as the **first** promise settles. Fulfilled or rejected, doesn't matter.

The classic use case is a timeout:

```js
const timeout = new Promise((resolve, reject) => {
  setTimeout(() => reject(new Error('timeout')), 5000)
})

const data = await Promise.race([fetchUsers(), timeout])
```

If `fetchUsers()` finishes within 5 seconds, you get the data. Otherwise the timeout rejects first and the `await` throws.

Be careful with the semantics here. If the fastest promise **rejects**, `race()` rejects, even if a slower promise would have succeeded a moment later. Race means race: the first one across the line decides the outcome, win or lose.

If that's not what you want, you want `Promise.any()`.

## Promise.any: first success wins

`Promise.any()` fulfills with the first promise that **fulfills**. Rejections are ignored, unless everything rejects.

Think of fetching the same file from multiple mirrors:

```js
const file = await Promise.any([
  fetch('https://cdn-eu.example.com/app.js'),
  fetch('https://cdn-us.example.com/app.js'),
])
```

If the EU mirror is down, no problem. As long as one mirror responds, you get its result.

If **every** promise rejects, `Promise.any()` rejects with an `AggregateError`. This is a special error that wraps all the individual failures:

```js
try {
  await Promise.any([failA(), failB()])
} catch (err) {
  console.log(err instanceof AggregateError) //true
  console.log(err.errors) //[Error from failA, Error from failB]
}
```

The individual errors are in the `errors` array. Log them, or you'll be debugging blind.

## Common mistakes

### Unhandled rejections with race()

This one bites in production. Say you race a fetch against a timeout, and the fetch wins:

```js
const data = await Promise.race([fetchUsers(), timeoutIn(5000)])
```

The timeout promise is still out there. When it rejects 5 seconds later, nothing is listening. In Node.js that's an unhandled rejection, and depending on your setup it can crash the process.

The fix is to make sure every promise you create has a rejection handler, or to make the timeout resolve instead of reject and check the result. `AbortController` with `AbortSignal.timeout()` is often the cleaner solution for fetch specifically.

### Awaiting in a loop instead of combining

If your operations don't depend on each other, don't await them one by one:

```js
//slow: sequential
const users = await fetchUsers()
const posts = await fetchPosts()

//fast: parallel
const [users, posts] = await Promise.all([fetchUsers(), fetchPosts()])
```

The [async/await syntax](https://flaviocopes.com/javascript-async-await/) makes sequential code so easy to write that people write it by accident.

### Forgetting that allSettled never throws

Wrapping `Promise.allSettled()` in a try/catch does nothing. It never rejects. If you're not checking each result's `status`, failures pass through your code silently.

## Which one should you use?

Ask two questions.

Do you need all results? If yes: `all()` when a failure should abort, `allSettled()` when partial success is fine.

Is one result enough? If yes: `race()` when you want the fastest settlement (timeouts), `any()` when you want the first success (fallbacks, mirrors).

I built a small [Promise combinator chooser and visualizer](https://flaviocopes.com/tools/promise-combinators/) that walks you through these questions and shows on a timeline how each combinator settles with the same set of promises. Try flipping one promise to "reject" and watch how differently the four behave.
