Learning Objectives

  • Understand Promise.allSettled() behavior
  • Learn the difference from Promise.all()
  • Handle mixed success/failure results
  • Implement batch operations with reporting

What is Promise.allSettled()?

Promise.allSettled() waits for all Promises to settle (either fulfill or reject), and returns an array describing the outcome of each Promise.

Promise.allSettled([promise1, promise2, promise3])
  .then(results => {
    // Always an array of result objects
    results.forEach(result => {
      if (result.status === 'fulfilled') {
        console.log('Success:', result.value);
      } else {
        console.log('Failed:', result.reason);
      }
    });
  });

Result Format

Each result object has a status property:

// Fulfilled Promise
{ status: 'fulfilled', value: result }

// Rejected Promise
{ status: 'rejected', reason: error }

Basic Example

const promises = [
  Promise.resolve('Success 1'),
  Promise.reject(new Error('Failed')),
  Promise.resolve('Success 2')
];

Promise.allSettled(promises)
  .then(results => {
    console.log(results);
    // [
    //   { status: 'fulfilled', value: 'Success 1' },
    //   { status: 'rejected', reason: Error: Failed },
    //   { status: 'fulfilled', value: 'Success 2' }
    // ]
  });

Promise.allSettled() vs Promise.all()

Feature Promise.allSettled() Promise.all()
Waits for All to settle All to fulfill
Rejects when Never Any rejects
Result Array of result objects Array of values
Use case Partial success OK All must succeed

Common Use Cases

1. Batch Operations with Reporting

async function batchDeleteUsers(userIds) {
  const deletePromises = userIds.map(id => 
    deleteUser(id).catch(error => error)
  );
  
  const results = await Promise.allSettled(deletePromises);
  
  const successful = results.filter(r => r.status === 'fulfilled');
  const failed = results.filter(r => r.status === 'rejected');
  
  console.log(`Deleted: ${successful.length}`);
  console.log(`Failed: ${failed.length}`);
  
  return {
    successful: successful.map(r => r.value),
    failed: failed.map(r => r.reason)
  };
}

2. Multiple API Calls with Fallbacks

async function fetchAllData() {
  const results = await Promise.allSettled([
    fetchWeather(),
    fetchNews(),
    fetchStocks()
  ]);
  
  return {
    weather: results[0].status === 'fulfilled' 
      ? results[0].value 
      : null,
    news: results[1].status === 'fulfilled' 
      ? results[1].value 
      : [],
    stocks: results[2].status === 'fulfilled' 
      ? results[2].value 
      : []
  };
}

3. Validation with Multiple Checks

async function validateUser(userData) {
  const validations = await Promise.allSettled([
    validateEmail(userData.email),
    validateUsername(userData.username),
    validatePassword(userData.password),
    checkUsernameAvailability(userData.username)
  ]);
  
  const errors = validations
    .filter(r => r.status === 'rejected')
    .map(r => r.reason.message);
  
  if (errors.length > 0) {
    throw new Error(`Validation failed: ${errors.join(', ')}`);
  }
  
  return true;
}

Processing Results

Separating Success and Failure

const results = await Promise.allSettled(promises);

const successful = results
  .filter(r => r.status === 'fulfilled')
  .map(r => r.value);

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

console.log('Successful:', successful);
console.log('Failed:', failed);

Creating a Summary Report

async function processWithReport(items, processor) {
  const promises = items.map(item => processor(item));
  const results = await Promise.allSettled(promises);
  
  return {
    total: results.length,
    successful: results.filter(r => r.status === 'fulfilled').length,
    failed: results.filter(r => r.status === 'rejected').length,
    results: results
  };
}

const report = await processWithReport(userIds, fetchUser);
console.log(`Processed ${report.successful}/${report.total} successfully`);

Real-World Example: Image Gallery Loader

async function loadImageGallery(imageUrls) {
  const loadPromises = imageUrls.map(url => 
    new Promise((resolve, reject) => {
      const img = new Image();
      img.onload = () => resolve({ url, img, loaded: true });
      img.onerror = () => reject({ url, loaded: false, error: 'Failed to load' });
      img.src = url;
    })
  );
  
  const results = await Promise.allSettled(loadPromises);
  
  const loaded = results
    .filter(r => r.status === 'fulfilled')
    .map(r => r.value);
  
  const failed = results
    .filter(r => r.status === 'rejected')
    .map(r => r.reason);
  
  // Display loaded images
  loaded.forEach(({ img }) => {
    document.getElementById('gallery').appendChild(img);
  });
  
  // Show error for failed images
  if (failed.length > 0) {
    console.warn(`Failed to load ${failed.length} images`);
  }
  
  return {
    loaded: loaded.length,
    failed: failed.length,
    total: imageUrls.length
  };
}

Polyfill for Older Browsers

if (!Promise.allSettled) {
  Promise.allSettled = function(promises) {
    return Promise.all(
      promises.map(p =>
        Promise.resolve(p)
          .then(value => ({ status: 'fulfilled', value }))
          .catch(reason => ({ status: 'rejected', reason }))
      )
    );
  };
}

Best Practices

  1. Use when partial success is acceptable - Don't fail the entire operation if some items fail
  2. Always check status - Don't assume all succeeded
  3. Provide meaningful feedback - Report both successes and failures
  4. Handle failures gracefully - Have fallback strategies

When to Use Each Method

// Use Promise.all() when:
// - All operations MUST succeed
// - Fail-fast behavior is desired
const [user, posts, comments] = await Promise.all([
  fetchUser(id),
  fetchPosts(id),
  fetchComments(id)
]);

// Use Promise.allSettled() when:
// - Partial success is acceptable
// - Need to know which succeeded/failed
const results = await Promise.allSettled([
  sendEmail(user1),
  sendEmail(user2),
  sendEmail(user3)
]);

// Use Promise.race() when:
// - Only need the fastest result
// - Implementing timeouts
const data = await Promise.race([
  fetchData(),
  timeout(5000)
]);

Key Takeaways

  • Promise.allSettled() never rejects
  • ✅ Returns array of result objects with status property
  • ✅ Perfect for batch operations where partial success is OK
  • ✅ Always check status before accessing value or reason
  • ✅ Use for operations that should complete independently
  • ✅ Great for reporting and analytics

Next Steps

Next, we'll learn about Promise.any() for getting the first successful result!