Asher Cohen
Back to posts

JavaScript Promise.race() Method

Get the first result from multiple competing promises

Introduction

Promise.race() is the competitive sibling of the Promise family. It returns as soon as the first promise settles—whether that's a success or failure. Think of it as a race where the first to cross the finish line wins.

Syntax

Promise.race(iterable);

Parameters:

  • iterable: An iterable (usually an array) of Promise objects

Returns:

  • A Promise that settles as soon as one of the input promises settles
  • Adopts the status and value/reason of the first settled promise

Basic Usage

const promise1 = new Promise((resolve) => setTimeout(resolve, 500, 'one'));
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, 'two'));

Promise.race([promise1, promise2]).then((value) => {
  console.log(value); // 'two' - promise2 was faster
});

Real-World Examples

Timeout Implementation

The most common use case: adding timeouts to promises.

function withTimeout(promise, ms) {
  const timeout = new Promise((_, reject) =>
    setTimeout(() => reject(new Error('Timeout')), ms),
  );

  return Promise.race([promise, timeout]);
}

// Usage
try {
  const data = await withTimeout(fetchData(), 5000);
  console.log(data);
} catch (error) {
  console.error(error.message); // 'Timeout' if > 5 seconds
}

Multiple API Endpoints

Fetch from multiple sources and use whoever responds first:

async function getCachedData(key) {
  const sources = [fetchFromCache(key), fetchFromCDN(key), fetchFromAPI(key)];

  try {
    return await Promise.race(sources);
  } catch (error) {
    // All sources failed
    throw new Error('All data sources failed');
  }
}

Redundant Services

async function getWeather(location) {
  const providers = [
    fetchWeatherOpenWeather(location),
    fetchWeatherWeatherAPI(location),
    fetchWeatherAccuWeather(location),
  ];

  // Use whoever responds fastest
  return await Promise.race(providers);
}

Promise.race() vs Other Methods

Promise.race() - First to Finish

// Returns first result (success or failure)
const result = await Promise.race([fast, slow]);

Promise.all() - All Must Succeed

// Waits for all, fails on first error
const results = await Promise.all([p1, p2, p3]);

Promise.allSettled() - Wait for All

// Waits for all, returns all results
const results = await Promise.allSettled([p1, p2, p3]);

Promise.any() - First Success (ES2021)

// Returns first success, ignores rejections
const result = await Promise.any([p1, p2, p3]);

Important Considerations

Failures Count

Promise.race() doesn't care about success vs failure—it's purely about who finishes first.

const slowSuccess = new Promise((resolve) =>
  setTimeout(() => resolve('success'), 1000),
);
const fastFailure = new Promise((_, reject) =>
  setTimeout(() => reject(new Error('fail')), 100),
);

try {
  await Promise.race([slowSuccess, fastFailure]);
} catch (error) {
  console.error(error.message); // 'fail' - failure won
}

Empty Array

Promise.race([]); // Creates a promise that never settles

All Promises Start Immediately

All promises in the race begin executing at once. The race doesn't control when they start—it only controls which result you get back.

Common Pitfalls

Assuming Success

// ❌ Dangerous - might get a rejection
const result = await Promise.race([task1(), task2()]);

// ✅ Safe - handle both outcomes
try {
  const result = await Promise.race([task1(), task2()]);
  // Handle success
} catch (error) {
  // Handle failure
}

Race Conditions

The name "race" is literal—be careful about race conditions in your application logic.

// Which one wins is non-deterministic
const result = await Promise.race([fetchFromServerA(), fetchFromServerB()]);
// Different runs might get different results

Lost Results

Only the first result matters. The other promises continue running, but their results are ignored.

// The slower promise still completes, but we ignore it
const winner = await Promise.race([fast(), slow()]);
// slow() still resolves, but we don't use its value

When to Use Promise.race()

Good use cases:

  • Implementing timeouts
  • Redundant/fallback services
  • Getting the fastest response from multiple sources
  • Canceling operations with a timeout

Avoid when:

  • You need all results
  • You need to ensure success
  • The promises have side effects you need to complete
  • You care about which specific promise wins

Advanced Pattern: Weighted Race

Race with preference for certain sources:

async function weightedRace(promises, preferredIndex = 0) {
  try {
    // Try preferred source first with short timeout
    return await withTimeout(promises[preferredIndex], 200);
  } catch {
    // Fall back to race of all
    return await Promise.race(promises);
  }
}

Conclusion

Promise.race() is a powerful tool for time-sensitive operations and redundancy. Use it when speed matters more than which specific source provides the result. Just remember: in this race, anyone can win—even failure.

#javascript #async #promises #performance