BRUCE_FEBRUCE_FE

EN/CH Mode

BRUCE_FE JS Interview Notes - Hand-written Promise.all Implementation

Learn how to hand-write JavaScript Promise.all method implementation, master techniques for handling multiple asynchronous tasks, and enhance frontend interview competitiveness.

影片縮圖

Lazy to read articles? Then watch videos!

Promise.all Method Overview

Promise.all is an important method in JavaScript for handling multiple Promises. It takes an array of Promises (or iterable object) and returns a new Promise that resolves when all input Promises resolve successfully, or rejects immediately when any Promise is rejected.

Characteristics of Promise.all

  • 1. Parallel Execution: All Promises start executing simultaneously
  • 2. Wait for All: Wait for all Promises to complete
  • 3. One Failure, All Fail: If any Promise rejects, the entire operation immediately rejects
  • 4. Maintain Result Order: Result array order matches input array order
  • 5. Non-Promise Values: Automatically wrapped with Promise.resolve()

Hand-writing Promise.all implementation is a popular topic in frontend interviews, testing understanding of Promises, asynchronous processing, and array operations.

Native Promise.all Usage

Before implementation, let's look at the native usage of Promise.all:

// Create three Promises
const promise1 = Promise.resolve(1);
const promise2 = new Promise((resolve) => setTimeout(() => resolve(2), 1000));
const promise3 = Promise.resolve(3);

// Use Promise.all to process three Promises in parallel
Promise.all([promise1, promise2, promise3])
  .then(results => {
    console.log(results); // [1, 2, 3] (order matches input array)
  })
  .catch(error => {
    console.error('At least one Promise was rejected', error);
  });

// Example of handling rejection
const promise4 = Promise.resolve(4);
const promise5 = Promise.reject('Error occurred');
const promise6 = Promise.resolve(6);

Promise.all([promise4, promise5, promise6])
  .then(results => {
    console.log('This will not execute');
  })
  .catch(error => {
    console.error(error); // 'Error occurred' (from rejected promise5)
  });

Hand-written Promise.all Implementation

Now let's implement the Promise.all method:

// Basic Promise.all implementation
function promiseAll(promises) {
  // Check if input is iterable
  if (!promises[Symbol.iterator]) {
    return Promise.reject(new TypeError('Parameter must be iterable'));
  }
  
  // Convert input to array
  const promiseArray = Array.from(promises);
  const len = promiseArray.length;
  
  // Create a new Promise
  return new Promise((resolve, reject) => {
    // Empty array returns empty result directly
    if (len === 0) {
      return resolve([]);
    }
    
    // Array to store results
    const results = new Array(len);
    // Count of completed Promises
    let resolvedCount = 0;
    
    // Process each Promise
    promiseArray.forEach((promise, index) => {
      // Wrap non-Promise values as Promises
      Promise.resolve(promise)
        .then(value => {
          // Store results in original order
          results[index] = value;
          resolvedCount++;
          
          // When all Promises complete, resolve the overall Promise
          if (resolvedCount === len) {
            resolve(results);
          }
        })
        .catch(error => {
          // If any Promise rejects, immediately reject the overall Promise
          reject(error);
        });
    });
  });
}

Let's test this implementation:

// Test cases
const p1 = Promise.resolve(1);
const p2 = new Promise(resolve => setTimeout(() => resolve(2), 100));
const p3 = 3; // Non-Promise value, will be automatically wrapped

promiseAll([p1, p2, p3])
  .then(results => {
    console.log(results); // [1, 2, 3]
  })
  .catch(error => {
    console.error(error);
  });

// Test error case
const p4 = Promise.resolve(4);
const p5 = Promise.reject('Failure');
const p6 = Promise.resolve(6);

promiseAll([p4, p5, p6])
  .then(results => {
    console.log('This will not execute');
  })
  .catch(error => {
    console.error(error); // 'Failure'
  });