Implementing a Promise.all Polyfill for Javascript Interviews

Β·

3 min read

Introduction

In this blog post, we'll walk through the step-by-step implementation of a polyfill for the Promise.all method in JavaScript. This polyfill allows developers to use Promise.all in environments where it is not natively supported, ensuring consistent behavior across different platforms.

Check for Existing Implementation

Before implementing the polyfill, it's important to check if the environment already supports Promise.all. If it doesn't, we can proceed with the polyfill implementation.

if (!Promise.all) {
  // Proceed with polyfill implementation
}

Create the Polyfill Function

Implementing the Promise.all polyfill involves several key steps to ensure correct behavior:

  • Input Validation: Check if the input is an array of promises. If not, reject the main promise with a TypeError.

  • Empty Array Input: Check if the input array is empty than resolve with empty array

  • Initialization: Initialize an empty array to store the results of each promise and a counter to keep track of how many promises have settled.

  • Iteration Over Promises: Use a loop to iterate over each promise in the input array. For each promise:

    • Use Promise.resolve to ensure the promise is a proper promise object.

    • Attach a then handler to handle fulfillment. Store the fulfillment value in the results array at the corresponding index.

    • Attach a catch handler to handle rejection. If any promise rejects, reject the main promise with the reason of the first rejection.

  • Completion Check: Increment the counter in the then and catch handlers and check if all promises have settled. If all promises have settled (i.e., the counter equals the number of promises), resolve the main promise with the results array.

Here's the detailed implementation

if (!Promise.all) {

Promise.all = function(promises) {
  return new Promise((resolve, reject) => {
    // Input validation
    if (!Array.isArray(promises)) {
      return reject(new TypeError('arguments must be an array'));
    }
    // return empty array if input is given as empty array 
    if(promises.length === 0) {
      resolve([])
    }

    let results = [];
    let completed = 0;

    // Iterate over promises
    promises.forEach((promise, index) => {
      // Ensure promise is a proper promise object
      Promise.resolve(promise)
        // Handle fulfillment
        .then((value) => {
          results[index] = value;
          completed++;
          // Check if all promises have settled
          if (completed === promises.length) {
            resolve(results);
          }
        })
        // Handle rejection
        .catch((reason) => {
          reject(reason);
        });
    });
  });
};   
}

Testing the Polyfill:

Testing is crucial to ensure that our polyfill works as expected. Let's add some test cases to cover different scenarios:

// Test case 1: All promises resolve
const promises1 = [
  Promise.resolve('Success 1'),
  Promise.resolve('Success 2'),
  Promise.resolve('Success 3')
];

Promise.all(promises1)
  .then(results => console.log(results))
  .catch(error => console.error(error));

// Test case 2: Some promises reject
const promises2 = [
  Promise.resolve('Success 1'),
  Promise.reject('Error 2'),
  Promise.resolve('Success 3')
];

Promise.all(promises2)
  .then(results => console.log(results))
  .catch(error => console.error(error));

// Test case 3: Empty array of promises
const promises3 = [];

Promise.all(promises3)
  .then(results => console.log(results))
  .catch(error => console.error(error));

These test cases cover scenarios where all promises resolve, some promises reject, and an empty array of promises is provided.

Additional Resources:

Β