🟨 JavaScript
Hard
🕐 15 min

Promise.all() Implementation

Goal: Understand the inner workings of Promise.all() and practice creating and managing promises.

💡 Hint
  1. Your promiseAll function should return a new Promise.
  2. Inside this promise, you need to iterate over the input promises array.
  3. Create a results array to store the results of the resolved promises and a completed counter to track the number of finished ones.
  4. For each item in the promises array:
    • Use Promise.resolve() to handle both promises and regular values.
    • In .then(), add the result to the results array, maintaining the original order. Increment the completed counter.
    • If completed equals the length of the promises array, it means all promises are resolved. Call resolve() with the results array.
    • In .catch(), immediately call reject() with the error.
  5. Don’t forget to handle the case where the input is an empty array. In this case, the promise should resolve immediately with an empty array.
👀 Solution
const promiseAll = (promises) => {
  // Return a new promise that will manage the asynchronous operation.
  return new Promise((resolve, reject) => {
    // An array to store the results of the resolved promises.
    const results = [];
    // A counter to track the number of resolved promises.
    let completed = 0;
    // The total number of promises in the array for convenience.
    const promisesCount = promises.length;
 
    // If the input array is empty, immediately resolve the promise with an empty array.
    // This matches the behavior of the native Promise.all().
    if (promisesCount === 0) {
      resolve([]);
      return;
    }
 
    // Iterate over each item in the input array.
    promises.forEach((promise, index) => {
      // Wrap each item in Promise.resolve().
      // This allows handling both actual promises and regular values (e.g., numbers, strings),
      // which will be treated as already resolved promises.
      Promise.resolve(promise)
        .then(value => {
          // When a promise resolves successfully, save its result.
          // The result is placed in the `results` array at the same index
          // as the original promise to maintain the order.
          results[index] = value;
          // Increment the counter of resolved promises.
          completed++;
 
          // Check if all promises have completed.
          // If the number of completed promises equals the total number,
          // it means all tasks have finished successfully.
          if (completed === promisesCount) {
            // Resolve the main promise, returning the array of results.
            resolve(results);
          }
        })
        // If any of the promises in the chain rejects,
        // we immediately reject the main promise.
        // The entire Promise.all() operation fails,
        // and the rejection reason of the first failed promise is returned.
        .catch(reject);
    });
  });
};

Solution Analysis:

  • Time Complexity: O(N), where N is the number of promises, plus the execution time of the longest promise.
  • Space Complexity: O(N) to store the results.

Task Description

You need to implement your own version of the built-in Promise.all() static method.

The Promise.all(iterable) method returns a single Promise that resolves when all of the promises in the iterable argument have resolved. It rejects with the reason of the first promise that rejects.

Examples

All promises resolve:

const p1 = Promise.resolve(3);
const p2 = 1337;
const p3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});
 
promiseAll([p1, p2, p3]).then(values => {
  console.log(values); // [3, 1337, "foo"]
});

One of the promises rejects:

const p1 = Promise.resolve(1);
const p2 = Promise.reject('error');
const p3 = Promise.resolve(3);
 
promiseAll([p1, p2, p3]).catch(error => {
  console.error(error); // "error"
});

Requirements

  • The function must be named promiseAll.
  • It must accept an iterable object (e.g., an array).
  • It must return a single Promise.
  • The returned promise must resolve with an array of results when all promises in the iterable have resolved. The order of the results must match the order of the promises.
  • If any of the promises in the iterable rejects, the returned promise must immediately reject with the rejection reason of the first promise that rejected.
  • If the iterable contains non-promise values, they should be treated as already resolved promises.
  • If the iterable is empty, the promise must resolve immediately with an empty array.

🧑‍💻 It's not a bug! It's a feature!

The code editor is intentionally hidden on mobile.

Believe me, it's for the best: I am protecting you from the temptation to code in less-than-ideal conditions. A small screen and a virtual keyboard are not the best tools for a programmer.

📖 Now: Study the task, think through the solution. Act like a strategist.

💻 Later: Sit down at your computer, open the site, and implement all your ideas comfortably. Act like a code-jedi!