Exploring Promise Methods and their Polyfills in JavaScript: all(), any(), allSettled() and race()

Exploring Promise Methods and their Polyfills in JavaScript: all(), any(), allSettled() and race()

In this article, I’ve explained the Promise methods: all(), any(), race(), allSettled() along with their polyfills.

Promise.all()

  • It accepts an iterable of promises as its argument and returns a Promise

  • If all promises resolve, then the returned promise is resolved with an array of fulfilled values. The order of the values is the same as that of the promises.

  • If any of the promises reject, then the returned promise is rejected with the reason of the first rejected promise.

Custom implementation:

Promise.myAll = function (promises) {
  const result = [];
  let resolvedCount = 0;

  if(promises.length === 0) {
    return Promise.resolve(promises)
  }

  return new Promise((resolve, reject) => {
    promises.forEach((promise, idx) => {
      Promise.resolve(promise)
        .then((res) => {
          result[idx] = res;
          resolvedCount++;

          if (resolvedCount === promises.length) {
            resolve(result);
          }
        })
        .catch((err) => {
            reject(err)
        });
    });
  });
};

Test cases:

Test case 1: If all the promises resolve

const case1 = [
  new Promise((resolve) => {
    timer2 = setTimeout(resolve, 3000, "resolved");
  }),
  200,
  Promise.resolve("Hello"),
];

Promise.myAll(case1)
  .then((res) => console.log(res))
  .catch((err) => console.log("Error: ", err));

// Output:
// [ "resolved", 200, "Hello"]

Test case 2: If any of the promises reject

const case2 = [
  Promise.reject("rejected"),
  new Promise((resolve, reject) => {
    timer2 = setTimeout(reject, 3000, "rejectedReason");
  }),
];

Promise.myAll(case2)
  .then((res) => console.log(res))
  .catch((err) => console.log("Error: ", err));

// Output:
// Error: rejected

Test case 3: If an empty array

const case3 = [];

Promise.myAll(case3)
  .then((res) => console.log(res))
  .catch((err) => console.log("Error: ", err));

// Output:
// []

Promise.any()

  • It accepts an iterable of promises as its argument and returns a Promise

  • The Promise resolves when the first promise in the array resolves. It is resolved with the value of the first promise.

  • The Promise rejects when all the promises are rejected. It is rejected with an AggregateError which includes all the rejected promises’ reasons.

Custom implementation:

Promise.myAny = (promises) => {
  const errorsList = new Array(promises.length);
  let counter = 0;

  return new Promise((resolve, reject) => {
    promises.forEach((promise, idx) => {
      Promise.resolve(promise)
        .then((res) => resolve(res))
        .catch((err) => {
          errorsList[idx] = err;
          counter++;

          if (counter === promises.length) {
            reject(
              new AggregateError(errorsList, "All promises were rejected")
            );
          }
        });
    });
  });
};

Test cases:

Test case 1: If any of the promises resolve

const case1 = [
  new Promise((resolve) => {
    timer2 = setTimeout(resolve, 3000, "resolved");
  }),
  200,
  Promise.reject("Hello"),
];

Promise.myAny(case1)
  .then((res) => console.log(res))
  .catch((err) => console.log("Error: ", err));

// Output:
// 200

Test case 2: If all of the promises reject

const case2 = [
  Promise.reject("rejected"),
  new Promise((resolve, reject) => {
    timer2 = setTimeout(reject, 3000, "rejectedReason");
  }),
];

Promise.myAny(case2)
  .then((res) => console.log(res))
  .catch((err) => console.log("Error: ", err.name, err.message, err.errors));

// Output:
// Error: AggregateError All promises were rejected ["rejected", "rejectedReason"]

Promise.race()

  • It accepts an iterable of promises as its argument and returns a Promise

  • It returns a Promise when any of the passed promises is settled.

  • If the first settled promise is resolved, the returned promise is resolved.

  • If the first settled promise is rejected, the returned promise is rejected.

Polyfill:

Promise.myRace = (promises) => {
  return new Promise((resolve, reject) => {
    promises.forEach((promise) => {
      Promise.resolve(promise).then(resolve, reject).catch(reject);
    });
  });
};

Test cases:

Test case 1: If the first settled promise resolves

const case1 = [
  new Promise((resolve) => {
    timer2 = setTimeout(resolve, 3000, "resolved");
  }),
  200,
  Promise.reject("Hello"),
];

Promise.myRace(case1)
  .then((res) => console.log(res))
  .catch((err) => console.log("Error: ", err));

// Output:
// 200

Test case 2: If the first settled promise rejects

const case2 = [
  Promise.reject("rejected"),
  new Promise((resolve) => {
    timer2 = setTimeout(resolve, 3000, "rejectedReason");
  }),
];

Promise.myRace(case2)
  .then((res) => console.log(res))
  .catch((err) => console.log("Error: ", err));

// Output:
// Error: rejected

Promise.allSettled()

  • It accepts an iterable of promises as its argument and returns a Promise

  • It returns a Promise when all of the passed promises are settled.

  • It is resolved with an array of values that provided the status of the promise i.e. whether it is fulfilled or rejected and the value or the reason with which the promise was fulfilled or rejected respectively.

Polyfill:

Promise.myAllSettled = (promises) => {
  const results = new Array(promises.length);
  let counter = 0;

  return new Promise((resolve, reject) => {
    promises.forEach((promise, index) => {
      Promise.resolve(promise)
        .then((res) => {
          results[index] = { status: "fulfilled", value: res };
        })
        .catch((err) => {
          results[index] = { status: "rejected", reason: err };
        })
        .finally(() => {
          counter++;
          if (counter === promises.length) {
            resolve(results);
          }
        });
    });
  });
};

Test cases:

Test case: When all promises settle

const case1 = [
  new Promise((resolve) => {
    timer2 = setTimeout(resolve, 3000, "resolved");
  }),
  200,
  Promise.reject("Hello"),
];

Promise.myAllSettled(case1)
  .then((res) => console.log(res))
  .catch((err) => console.log("Error: ", err));

// Output:
// [
//  { status: 'fulfilled', value: 'resolved' },
//  { status: 'fulfilled', value: 200 },
//  { status: 'rejected', reason: 'Hello' }
// ]

That’s all folks 👋

Hope you liked this article! If there are any improvements that can be made in the polyfills, feel free to raise an issue here 👇

Let’s connect: