JavaScript Promises: race(), all(), allSettled(), and then() Explained

Master the essential Promise concurrency methods for building performant modern web applications with Next.js

Understanding Promises in Modern JavaScript

Promises have become the backbone of asynchronous JavaScript, providing a cleaner alternative to callback-based patterns. With the widespread adoption of async/await syntax, understanding how Promises work under the hood remains crucial for every JavaScript developer.

In Next.js applications, you'll frequently encounter scenarios where multiple async operations need coordination--fetching several API endpoints simultaneously, loading data for server-side rendering, or handling concurrent user requests. For teams building modern web applications, mastering Promise methods is essential for optimal performance.

The JavaScript runtime provides several static methods on the Promise constructor that help you coordinate multiple promises effectively: Promise.all(), Promise.allSettled(), Promise.race(), and Promise.then(). Each serves distinct purposes and understanding when to use each one is key to writing efficient, maintainable async code.

Quick Reference: When to Use Each Method

MethodUse WhenBehavior
Promise.all()All operations MUST succeedRejects if ANY promise rejects
Promise.allSettled()Operations are independentWaits for ALL to settle
Promise.race()You need the FIRST resultResolves with first settled promise
Promise.then()Chaining operationsReturns new promise for chaining

Promise.all(): Execute Operations in Parallel

What Promise.all() Does

Promise.all() takes an iterable of promises as input and returns a single Promise that fulfills when all of the input's promises fulfill. The returned promise resolves to an array containing all the fulfillment values, in the same order as the input promises.

This method is particularly useful when you have multiple independent async operations that must all complete successfully before proceeding. However, Promise.all() rejects immediately when ANY of the input promises rejects--this fail-fast behavior makes it ideal for scenarios where all operations are critical.

// Fetching multiple API endpoints in parallel
async function fetchPageData() {
 const [userData, productData, settingsData] = await Promise.all([
 fetch('/api/user').then(r => r.json()),
 fetch('/api/products').then(r => r.json()),
 fetch('/api/settings').then(r => r.json())
 ]);

 return { userData, productData, settingsData };
}

Error Handling with Promise.all()

When any promise in the array rejects, Promise.all() immediately rejects with that first rejection reason. You won't get results from the other promises that might have succeeded.


Promise.allSettled(): Handle All Results Independently

Introduction to Promise.allSettled()

Promise.allSettled() waits for all promises in the iterable to settle--meaning they either fulfill or reject--before the returned promise fulfills. Unlike Promise.all(), it doesn't reject when individual promises fail.

This method was introduced to JavaScript in 2020 for scenarios where you need results from all operations, regardless of individual outcomes. It's valuable when making several API calls that don't depend on each other, especially when building full-stack JavaScript applications that aggregate data from multiple sources.

Return Value Structure

// Each outcome object has:
{
 status: 'fulfilled' | 'rejected', // Always present
 value: any, // Present if fulfilled
 reason: any // Present if rejected
}
async function loadOptionalFeatures() {
 const results = await Promise.allSettled([
 fetch('/api/premium-features').then(r => r.json()),
 fetch('/api/recommendations').then(r => r.json()),
 fetch('/api/social-data').then(r => r.json())
 ]);

 const features = results[0].status === 'fulfilled' ? results[0].value : null;
 const recommendations = results[1].status === 'fulfilled' ? results[1].value : [];
 const socialData = results[2].status === 'fulfilled' ? results[2].value : {};

 return { features, recommendations, socialData };
}

When to Prefer allSettled() Over all()

  • Operations are independent and can succeed or fail separately
  • You want to implement graceful fallback or retry logic
  • You're loading optional features that shouldn't block functionality
  • You need to collect all errors for logging

Promise.race(): Get the First Result

Understanding Promise.race()

Promise.race() returns a Promise that settles as soon as any of the input promises settles. The returned promise adopts the state and value/reason of that first settled promise.

Practical Use Case: Request Timeouts

One common application is implementing request timeouts:

function fetchWithTimeout(url, timeoutMs = 5000) {
 const timeoutPromise = new Promise((_, reject) => {
 setTimeout(() => reject(new Error('Request timeout')), timeoutMs);
 });

 return Promise.race([
 fetch(url).then(r => r.json()),
 timeoutPromise
 ]);
}

Implementing Fallbacks with Promise.race()

async function getUserData() {
 const freshData = fetch('/api/user').then(r => ({ source: 'network', data: r.json() }));
 const cachedData = caches.match('/api/user').then(r => r ? ({ source: 'cache', data: r.json() }) : null);

 const result = await Promise.race([freshData, cachedData]);
 return result.data;
}

Promise.then(): The Foundation of Promise Chains

Chaining and Composition

Promise.then() is the most fundamental Promise method, allowing you to attach fulfillment and rejection handlers. It returns a new promise, enabling powerful chaining patterns.

fetch('/api/data')
 .then(response => {
 if (!response.ok) throw new Error('Network response was not ok');
 return response.json();
 })
 .then(data => {
 console.log('Data loaded:', data);
 return processData(data);
 })
 .catch(error => {
 console.error('Error in chain:', error);
 });

Separate Error Handling

fetch('/api/user')
 .then(
 user => validateUser(user),
 error => handleNetworkError(error)
 )
 .then(
 validatedUser => saveUser(validatedUser),
 error => handleValidationError(error)
 );

Connection to async/await

Under the hood, async/await is syntactic sugar over Promise.then() chains:

// This async/await code:
const data = await fetchData();
processData(data);

// Desugars to this:
fetchData().then(data => processData(data));

Performance Considerations for Next.js Applications

Parallel vs Sequential Execution

All static Promise methods execute input promises in parallel:

// SLOW: Sequential
const slow = await fetch1();
const slower = await fetch2();

// FAST: Parallel
const [fast, faster] = await Promise.all([fetch1(), fetch2()]);

Server-Side Rendering Considerations

  • Use Promise.all() for critical data required in initial render
  • Use Promise.allSettled() for non-blocking data
  • Implement timeouts with Promise.race() for API calls

When building full-stack JavaScript applications, proper use of these Promise methods can significantly impact performance and user experience. For applications requiring AI-powered automation, efficient async patterns are critical for handling multiple API calls to external services.


Conclusion

Mastering JavaScript's Promise concurrency methods is essential for building responsive web applications:

  • Promise.all() for when you need all operations to succeed together
  • Promise.allSettled() for independent operations where you want all results
  • Promise.race() for timeouts and getting the first result
  • Promise.then() for chaining and granular error handling

Choose the right method based on your requirements for success/failure handling and user experience.

Key Takeaways

Choose the right Promise method for your use case

Promise.all()

Use when ALL operations must succeed. Rejects immediately if any promise fails.

Promise.allSettled()

Use for independent operations. Returns results from all promises regardless of success or failure.

Promise.race()

Use for timeouts or when you need the first result. Resolves with whichever promise settles first.

Promise.then()

The foundation for chaining. Allows separate handling of success and failure cases.

const [users, products, orders] = await Promise.all([
 fetch('/api/users').then(r => r.json()),
 fetch('/api/products').then(r => r.json()),
 fetch('/api/orders').then(r => r.json())
]);
// All succeed or reject together

Frequently Asked Questions

What is the difference between Promise.all() and Promise.allSettled()?

Promise.all() rejects immediately if any promise rejects, while Promise.allSettled() waits for all promises to settle (fulfill or reject) and returns an array of outcome objects. Use allSettled() when you need results from all operations regardless of individual success or failure.

When should I use Promise.race()?

Use Promise.race() for implementing request timeouts, falling back to cached data, or when you only need the first operation to complete. It's commonly used to prevent hanging indefinitely on slow network requests.

Does Promise.all() run promises in parallel?

Yes, Promise.all() executes all input promises concurrently, not sequentially. However, the promises must be created before being passed to Promise.all()--if you create them inside the array, they start executing immediately.

How does async/await relate to Promise methods?

async/await is syntactic sugar over Promises. When you write `await fetchData()`, it's equivalent to `fetchData().then(...)`. Understanding this helps debug async code and work with promise-based APIs.

Ready to Build High-Performance Web Applications?

Our team specializes in modern JavaScript development, including Next.js applications with optimal async patterns.

Sources

  1. MDN Web Docs - Promise.all() - Official JavaScript documentation for Promise.all() behavior and syntax
  2. MDN Web Docs - Promise.allSettled() - Official documentation for Promise.allSettled() including return value structure
  3. MDN Web Docs - Promise.race() - Official documentation for Promise.race() concurrency method
  4. MDN Web Docs - Promise.then() - Official documentation for Promise.then() method
  5. LogRocket Blog - Is Promise.all still relevant in 2025? - Modern perspective on Promise.all usage patterns in JavaScript development