Promise.all vs Promise.allSettled vs Promise.race vs Promise.any

tutorialMay 11, 2026· 5 min read

You've got multiple async operations to run in parallel. Do you reach for Promise.all, Promise.allSettled, Promise.race, or Promise.any?

They all do similar things — run Promises in parallel — but they behave differently when things go wrong. Pick the wrong one, and you'll silently swallow errors or throw away perfectly good data.

Here's a practical guide to choosing the right Promise combinator.

Quick Comparison

MethodAll must succeed?Fails fast?Reject valueFulfills with
Promise.allYesYesFirst rejectionArray of results
Promise.allSettledNoNoNever rejectsArray of {status, value/reason}
Promise.raceNoYesFirst rejection OR fulfillmentFirst settled value
Promise.anyAt least oneNoAggregateErrorFirst fulfillment

Let's break down each one with real examples.

Promise.all: All or Nothing

Use Promise.all when you need all operations to succeed. If one fails, the entire batch fails.

const fetchUser = () => fetch('/api/user').then(r => r.json());
const fetchPosts = () => fetch('/api/posts').then(r => r.json());
const fetchSettings = () => fetch('/api/settings').then(r => r.json());

// All three must succeed
Promise.all([fetchUser(), fetchPosts(), fetchSettings()])
  .then(([user, posts, settings]) => {
    // user = user data
    // posts = posts array
    // settings = settings object
    renderDashboard(user, posts, settings);
  })
  .catch(err => {
    // If ANY fetch fails, you land here
    showErrorMessage('Failed to load dashboard');
  });

When to use it:

  • Loading dependent data (dashboard needs user + posts + settings)
  • Batch operations where partial success is useless
  • Parallel API calls that all must complete

Gotcha: Promise.all fails fast. The first rejection stops everything, even if other Promises would have succeeded.

// Promise 1 resolves after 2s
// Promise 2 rejects after 1s
// Promise 3 resolves after 3s

Promise.all([
  delay(2000).then(() => 'one'),
  delay(1000).then(() => Promise.reject(new Error('two failed'))),
  delay(3000).then(() => 'three')
])
  .catch(err => {
    console.log(err.message); // "two failed"
    // "one" and "three" are discarded
  });

Promise.allSettled: Don't Fail Fast

Use Promise.allSettled when you want to run all Promises and handle each result individually. It never rejects — you always get an array of settled statuses.

const urls = ['/api/a', '/api/b', '/api/c', '/api/d'];

Promise.allSettled(urls.map(url => fetch(url)))
  .then(results => {
    results.forEach((result, index) => {
      if (result.status === 'fulfilled') {
        console.log(`${urls[index]}:`, result.value);
      } else {
        console.error(`${urls[index]} failed:`, result.reason);
      }
    });
  });

When to use it:

  • Batch operations where partial success is okay
  • Running independent tasks and aggregating results
  • Logging/reporting where you want to see all outcomes

Gotcha: You get objects, not raw values:

const results = await Promise.allSettled([
  Promise.resolve('success'),
  Promise.reject(new Error('failed'))
]);

// results[0] = { status: 'fulfilled', value: 'success' }
// results[1] = { status: 'rejected', reason: Error('failed') }

Promise.race: First to Settle Wins

Use Promise.race when you only care about the first result, whether it's a success or failure. It's a race — the first Promise to settle wins.

// Fetch with timeout
const fetchWithTimeout = (url, timeout = 5000) => {
  return Promise.race([
    fetch(url),
    new Promise((_, reject) =>
      setTimeout(() => reject(new Error('Timeout')), timeout)
    )
  ]);
};

fetchWithTimeout('/api/slow-endpoint', 3000)
  .then(response => console.log('Got response:', response))
  .catch(err => console.error('Failed:', err.message));

When to use it:

  • Implementing timeouts
  • Caching (race between cache and network)
  • Trying multiple sources and taking the first response

Gotcha: The first Promise to settle wins, not the first to fulfill. A rejection wins over a slower fulfillment:

Promise.race([
  delay(1000).then(() => 'slow success'),
  delay(500).then(() => Promise.reject(new Error('fast failure')))
])
  .catch(err => {
    console.log(err.message); // "fast failure" — even though it's a rejection
  });

Promise.any: First Success Wins

Use Promise.any when you want the first successful result and you're okay with some failures. It rejects only if all Promises fail.

// Try multiple servers, use the first that responds
const servers = [
  'https://api1.example.com/data',
  'https://api2.example.com/data',
  'https://api3.example.com/data'
];

Promise.any(servers.map(url => fetch(url)))
  .then(response => response.json())
  .then(data => console.log('Got data from:', data))
  .catch(err => {
    if (err instanceof AggregateError) {
      console.error('All servers failed:', err.errors);
    }
  });

When to use it:

  • Fallback strategies (try multiple sources)
  • Fastest available CDN endpoint
  • Any-success-is-enough scenarios

Gotcha: If all Promises reject, you get an AggregateError containing all rejection reasons:

Promise.any([
  Promise.reject(new Error('Server 1 failed')),
  Promise.reject(new Error('Server 2 failed')),
  Promise.reject(new Error('Server 3 failed'))
])
  .catch(err => {
    console.log(err instanceof AggregateError); // true
    console.log(err.errors); // [Error('Server 1 failed'), Error('Server 2 failed'), Error('Server 3 failed')]
  });

Real-World Example: Image Gallery with Fallbacks

Here's a practical example combining multiple combinators:

async function loadGalleryWithFallbacks(imageUrls, timeout = 3000) {
  // 1. Try to fetch all images with timeout
  const fetchWithTimeout = (url) => Promise.race([
    fetch(url),
    new Promise((_, reject) =>
      setTimeout(() => reject(new Error('Timeout')), timeout)
    )
  ]);

  // 2. Get results for all images (don't fail fast)
  const results = await Promise.allSettled(
    imageUrls.map(url => fetchWithTimeout(url))
  );

  // 3. Separate successes and failures
  const successful = results
    .filter(r => r.status === 'fulfilled')
    .map(r => r.value);

  const failed = results
    .filter(r => r.status === 'rejected')
    .map(r => r.reason);

  // 4. If we have some success, use them
  if (successful.length > 0) {
    console.log(`Loaded ${successful.length}/${imageUrls.length} images`);
    return successful;
  }

  // 5. If all failed, try fallback URLs (first success wins)
  const fallbackUrls = imageUrls.map(url => url.replace('/images/', '/fallback/'));
  try {
    const fallbackResult = await Promise.any(
      fallbackUrls.map(url => fetch(url))
    );
    console.log('Using fallback image');
    return [fallbackResult];
  } catch (err) {
    // Even fallbacks failed
    console.error('All images and fallbacks failed:', err.errors);
    return [];
  }
}

Which One Should You Use?

ScenarioUse
Need all results to succeedPromise.all
Want all results, even if some failPromise.allSettled
Only need first result (success or fail)Promise.race
Need first success, failures okayPromise.any
Implementing a timeoutPromise.race
Fallback strategiesPromise.any
Batch independent operationsPromise.allSettled

Common Mistakes

Mistake 1: Using Promise.all when you don't need all results

// Bad: Loading 10 avatars, but you're okay with some failing
Promise.all(avatars.map(loadAvatar))
  .then(allAvatars => displayAvatars(allAvatars))
  .catch(err => {
    // One failed avatar = no avatars at all
    displayError();
  });

// Good: Use allSettled to show what you can
Promise.allSettled(avatars.map(loadAvatar))
  .then(results => {
    const loaded = results.filter(r => r.status === 'fulfilled').map(r => r.value);
    displayAvatars(loaded);
  });

Mistake 2: Forgetting that Promise.race rejects on first failure

// Bad: Expecting race to give you the first success
Promise.race([
  fetch('/api/fast-but-flaky'),
  fetch('/api/slow-but-reliable')
])
  .then(res => console.log('Got response'))
  .catch(err => {
    // If the fast-but-flaky fails first, you never try the slow-but-reliable
  });

// Good: Use Promise.any for first-success semantics
Promise.any([
  fetch('/api/fast-but-flaky'),
  fetch('/api/slow-but-reliable')
])
  .then(res => console.log('Got response'));

Mistake 3: Not handling empty arrays

// Bad: Promise.all with empty array resolves immediately
Promise.all([]).then(results => {
  console.log(results); // [] — might not be what you expect
});

// Be explicit about empty cases
const promises = urls.map(fetch);
if (promises.length === 0) {
  return [];
}
return Promise.all(promises);

Summary

  • Promise.all: All must succeed, fails fast. Use when you need everything.
  • Promise.allSettled: Runs all, never rejects. Use for batch operations where partial success is okay.
  • Promise.race: First to settle wins. Use for timeouts and first-response scenarios.
  • Promise.any: First success wins. Use for fallbacks and fastest-available scenarios.

Pick the right tool for the job, and you'll write more resilient async code.