What is Promise.all() used for

👨‍💻 Frontend Developer 🟠 May come up 🎚️ Medium
#JavaScript #Asynchronicity #JS Basics

Brief Answer

Promise.all() is a static method of the Promise class that takes an array of promises and returns a new promise. This new promise resolves when all promises in the array are fulfilled, or rejects if at least one of the promises is rejected. Its main purpose is to execute multiple asynchronous operations in parallel and get their results in one place.


Detailed Explanation

What is Promise.all()

Promise.all() is a powerful tool for working with multiple asynchronous operations simultaneously. It takes an iterable object (usually an array) of promises and returns a new promise that:

  1. Resolves successfully when all promises in the array are fulfilled successfully, returning an array of their results in the same order as the original promises.
  2. Rejects if at least one of the promises rejects, returning the reason for rejection of the first rejected promise.
const promises = [
  Promise.resolve(1),
  Promise.resolve(2),
  Promise.resolve(3)
];
 
Promise.all(promises)
  .then(results => {
    console.log(results); // [1, 2, 3]
  })
  .catch(error => {
    console.error(error);
  });

What is Promise.all() used for

1. Parallel Task Execution

The main advantage of Promise.all() is the ability to execute multiple asynchronous operations in parallel rather than sequentially, which significantly speeds up execution:

// Sequential execution (slow)
async function sequential() {
  const start = Date.now();
  
  const result1 = await fetch('/api/data1');
  const data1 = await result1.json();
  
  const result2 = await fetch('/api/data2');
  const data2 = await result2.json();
  
  const result3 = await fetch('/api/data3');
  const data3 = await result3.json();
  
  console.log('Execution time:', Date.now() - start);
  return [data1, data2, data3];
}
 
// Parallel execution with Promise.all() (fast)
async function parallel() {
  const start = Date.now();
  
  const [data1, data2, data3] = await Promise.all([
    fetch('/api/data1').then(res => res.json()),
    fetch('/api/data2').then(res => res.json()),
    fetch('/api/data3').then(res => res.json())
  ]);
  
  console.log('Execution time:', Date.now() - start);
  return [data1, data2, data3];
}

2. Synchronizing Dependent Operations

Promise.all() allows you to wait for all necessary operations to complete before continuing code execution:

async function loadUserData(userId) {
  // Load user profile, posts, and friends in parallel
  const [userProfile, userPosts, userFriends] = await Promise.all([
    fetchUserProfile(userId),
    fetchUserPosts(userId),
    fetchUserFriends(userId)
  ]);
  
  // Continue only when all data is loaded
  renderUserPage(userProfile, userPosts, userFriends);
}

3. Processing Data Arrays

Promise.all() is ideal for asynchronous processing of data arrays:

async function processItems(items) {
  // Create an array of promises for each item
  const promises = items.map(item => processItem(item));
  
  // Process all items in parallel
  const results = await Promise.all(promises);
  return results;
}
 
async function processItem(item) {
  // Asynchronous processing of a single item
  await new Promise(resolve => setTimeout(resolve, 100));
  return item * 2;
}
 
// Usage
processItems([1, 2, 3, 4, 5])
  .then(results => console.log(results)); // [2, 4, 6, 8, 10]

Features and Limitations of Promise.all()

1. Error Handling

Promise.all() has “fail-fast” behavior — it rejects as soon as any of the promises rejects:

const promises = [
  Promise.resolve(1),
  Promise.reject(new Error('Something went wrong')),
  Promise.resolve(3)
];
 
Promise.all(promises)
  .then(results => {
    console.log(results); // This code won't execute
  })
  .catch(error => {
    console.error(error); // Error: Something went wrong
  });

If you need to get the results of all promises regardless of their status, use Promise.allSettled().

2. Preserving Result Order

Promise.all() preserves the order of results according to the order of the original promises, even if they complete in a different order:

const promise1 = new Promise(resolve => setTimeout(() => resolve('first'), 300));
const promise2 = new Promise(resolve => setTimeout(() => resolve('second'), 200));
const promise3 = new Promise(resolve => setTimeout(() => resolve('third'), 100));
 
Promise.all([promise1, promise2, promise3])
  .then(results => {
    console.log(results); // ['first', 'second', 'third']
  });

3. Empty Array

If an empty array is passed, Promise.all() will immediately resolve with an empty array of results:

Promise.all([])
  .then(results => {
    console.log(results); // []
  });

4. Non-Promises in the Array

If there are non-promises in the array, they are automatically wrapped in Promise.resolve():

Promise.all([1, Promise.resolve(2), 3])
  .then(results => {
    console.log(results); // [1, 2, 3]
  });

Comparison with Other Promise Methods

Promise.all() vs Promise.race()

  • Promise.all() — waits for all promises to complete
  • Promise.race() — completes as soon as the first promise completes (successfully or with an error)
const promises = [
  new Promise(resolve => setTimeout(() => resolve('fast'), 100)),
  new Promise(resolve => setTimeout(() => resolve('medium'), 200)),
  new Promise(resolve => setTimeout(() => resolve('slow'), 300))
];
 
Promise.race(promises)
  .then(result => console.log(result)); // 'fast'

Promise.all() vs Promise.allSettled()

  • Promise.all() — rejects on the first error
  • Promise.allSettled() — always resolves successfully, returning the status and result/error for each promise
const promises = [
  Promise.resolve('success'),
  Promise.reject('error'),
  Promise.resolve('another success')
];
 
Promise.allSettled(promises)
  .then(results => {
    console.log(results);
    // [
    //   { status: 'fulfilled', value: 'success' },
    //   { status: 'rejected', reason: 'error' },
    //   { status: 'fulfilled', value: 'another success' }
    // ]
  });

Promise.all() vs Promise.any()

  • Promise.all() — succeeds when all promises succeed
  • Promise.any() — succeeds when at least one promise succeeds
const promises = [
  Promise.reject('error 1'),
  Promise.resolve('success'),
  Promise.reject('error 2')
];
 
Promise.any(promises)
  .then(result => console.log(result)); // 'success'

Practical Usage Examples

Loading Dashboard Data

async function loadDashboardData() {
  try {
    const [userData, statsData, notificationsData] = await Promise.all([
      fetchUserData(),
      fetchStatistics(),
      fetchNotifications()
    ]);
    
    renderDashboard(userData, statsData, notificationsData);
  } catch (error) {
    showErrorMessage('Failed to load dashboard data');
    console.error(error);
  }
}

Loading and Processing Images

async function loadAndProcessImages(imageUrls) {
  const imagePromises = imageUrls.map(async url => {
    const response = await fetch(url);
    const blob = await response.blob();
    return processImage(blob);
  });
  
  return Promise.all(imagePromises);
}
 
function processImage(blob) {
  return new Promise((resolve) => {
    const img = new Image();
    img.onload = () => {
      // Process the image
      const processedImage = applyFilters(img);
      resolve(processedImage);
    };
    img.src = URL.createObjectURL(blob);
  });
}

Form Data Validation

async function validateForm(formData) {
  const validationPromises = [
    validateUsername(formData.username),
    validateEmail(formData.email),
    validatePassword(formData.password),
    checkUsernameAvailability(formData.username)
  ];
  
  try {
    await Promise.all(validationPromises);
    return { valid: true };
  } catch (error) {
    return { valid: false, error: error.message };
  }
}

Best Practices

✅ Recommendations

  1. Use for independent operations — Promise.all() is ideal when operations don’t depend on each other
  2. Add error handling — always use try/catch or .catch() to handle possible errors
  3. Combine with async/await — for more readable code
  4. Limit the number of parallel requests — too many simultaneous operations can overload the system
// Limiting the number of parallel requests
async function processInBatches(items, batchSize = 5) {
  const results = [];
  
  for (let i = 0; i < items.length; i += batchSize) {
    const batch = items.slice(i, i + batchSize);
    const batchPromises = batch.map(item => processItem(item));
    const batchResults = await Promise.all(batchPromises);
    results.push(...batchResults);
  }
  
  return results;
}

❌ Anti-patterns

  1. Ignoring errors — don’t use Promise.all() without error handling
  2. Sequential operations — don’t use for operations that should be performed sequentially
  3. Mixing with other methods — don’t mix Promise.all() with other methods in a single chain without a clear understanding of the consequences
// ❌ Bad - ignoring errors
Promise.all(promises).then(results => {
  // What if an error occurs?
});
 
// ✅ Good - error handling
Promise.all(promises)
  .then(results => {
    // Process results
  })
  .catch(error => {
    // Handle errors
  });

Conclusion

Promise.all() is a powerful tool for parallel execution of asynchronous operations in JavaScript. It allows you to significantly speed up the execution of independent tasks, synchronize dependent operations, and efficiently process data arrays. Understanding the features and limitations of Promise.all(), as well as its differences from other Promise methods, allows you to create more efficient and reliable asynchronous code.

When used correctly with appropriate error handling, Promise.all() becomes an indispensable tool in every JavaScript developer’s arsenal, especially when working with APIs, processing data, and creating modern web applications.