Implementing Promise.all in JavaScript



What is Promise.all?

Promise.all is a method that takes an iterable (usually an array) of promises and returns a single promise that resolves when all of the promises in the iterable have resolved. It rejects if any of the promises in the iterable reject. The resolved value is an array of the resolved values of the input promises.

Real Interview Insights

Interviewers might ask you to:

  • Implement a simplified version of Promise.all.
  • Explain how Promise.all handles both resolved and rejected promises.
  • Discuss the performance and use cases of Promise.all.

Implementing Promise.all

Let's start with a basic implementation of Promise.all:

function promiseAll(promises) {
  return new Promise((resolve, reject) => {
    if (!Array.isArray(promises)) {
      return reject(new TypeError('Input must be an array'));
    }
 
    let resolvedCounter = 0;
    const resultArray = [];
    const promisesCount = promises.length;
 
    if (promisesCount === 0) {
      return resolve([]);
    }
 
    promises.forEach((promise, index) => {
      Promise.resolve(promise)
        .then(value => {
          resolvedCounter++;
          resultArray[index] = value;
 
          if (resolvedCounter === promisesCount) {
            resolve(resultArray);
          }
        })
        .catch(reject);
    });
  });
}
Explanation:
  • Input Validation: Ensure the input is an array. If not, reject with a TypeError.
  • Counter and Result Array: Use a counter to track the number of resolved promises and an array to store the resolved values.
  • Empty Array Check: If the input array is empty, resolve with an empty array immediately.
  • Promise Resolution: Use Promise.resolve to handle both promise and non-promise values, ensuring all values are treated as promises.

Practical Example

Consider an example with multiple promises:

const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = Promise.resolve(3);
 
promiseAll([promise1, promise2, promise3])
  .then(results => console.log(results)) // Output: [1, 2, 3]
  .catch(error => console.error(error));

In this example:

  • The promiseAll function combines three resolved promises into a single promise that resolves with an array of their values.

Advanced Use Case: Handling Rejections

To handle scenarios where one or more promises reject, let's ensure our promiseAll implementation properly rejects as soon as any promise rejects:

const failingPromise = new Promise((resolve, reject) => setTimeout(reject, 100, 'Failed'));
 
promiseAll([promise1, failingPromise, promise2])
  .then(results => console.log(results))
  .catch(error => console.error(error)); // Output: 'Failed'

In this scenario:

  • The promiseAll function rejects as soon as failingPromise rejects, demonstrating proper handling of rejected promises.

Performance Considerations

While Promise.all is efficient for waiting on multiple promises, be aware of the following:

  • Concurrent Execution: All promises are started concurrently, which can be resource-intensive.
  • Memory Usage: Storing all resolved values can consume significant memory for large arrays of promises.

Coding Challenge: Implement Promise.allSettled

Challenge: Implement Promise.allSettled, a method that returns a promise that resolves after all of the given promises have either resolved or rejected, with an array of objects describing the outcome of each promise.

function promiseAllSettled(promises) {
  return new Promise((resolve) => {
    if (!Array.isArray(promises)) {
      throw new TypeError('Input must be an array');
    }
 
    const resultArray = [];
    let settledCounter = 0;
    const promisesCount = promises.length;
 
    if (promisesCount === 0) {
      return resolve([]);
    }
 
    promises.forEach((promise, index) => {
      Promise.resolve(promise)
        .then(value => {
          resultArray[index] = { status: 'fulfilled', value };
          settledCounter++;
          if (settledCounter === promisesCount) {
            resolve(resultArray);
          }
        })
        .catch(reason => {
          resultArray[index] = { status: 'rejected', reason };
          settledCounter++;
          if (settledCounter === promisesCount) {
            resolve(resultArray);
          }
        });
    });
  });
}
 
// Example usage with allSettled
promiseAllSettled([promise1, failingPromise, promise2])
  .then(results => console.log(results));

In this challenge:

  • The promiseAllSettled function waits for all promises to settle and returns an array describing each promise's outcome.