Search code examples
javascriptpromisesettimeoutpolyfillspromise.all

How to handle setTimeout case while writing Polyfill for Promise.all in Javascript


I am trying to write a polyfill for Promise.all, it is working fine when I passed promises without setTimeout, but with setTimeout, I am not getting the correct results since it resolves and returns the promise before the timer expires.

How can this case be handled to work in the below function, in the same way as actual Promise.all works.

Below is my code snippet and link to codesandbox

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("success 1"), 2000);
  });

const promise2 = new Promise((resolve, reject) => {
  resolve("success 2");
});

const promise3 = new Promise((resolve, reject) => {
  resolve("success 3");
});
    
 function resolveAll(promiseArr) {
      let arr = [];
      return new Promise((resolve, reject) => {
        promiseArr.map((each_promise, index) => {
          return each_promise
            .then((res) => {
              arr[index] = res;
              if (index === promiseArr.length - 1) {
                resolve(arr);
              }
            })
            .catch((err) => {
              reject(err);
            });
        });
      });
    }
 resolveAll([promise1, promise2, promise3])
  .then((res) => {
    console.log(res, "result");
  })
  .catch((err) => console.log(err, "error"));

Actual result : [undefined, "success 2", "success 3"]

Expected result: ["success 1", "success 2", "success 3"]

https://codesandbox.io/s/fast-bush-6osdy?file=/src/index.js


Solution

  • Your problem is that

    if (index === promiseArr.length - 1) {
        resolve(arr);
    }
    

    just checks whether the last promise has resolved, but in your scenario the first promise is the last one that gets resolved because of the setTimeout.

    One solution would be to keep a count of how many of the promises have resolved, e.g.

    const promise1 = new Promise((resolve, reject) => {
        setTimeout(() => resolve("success 1"), 2000);
    });
    
    const promise2 = new Promise((resolve, reject) => {
        resolve("success 2");
    });
    
    const promise3 = new Promise((resolve, reject) => {
      resolve("success 3");
    });
    
    function resolveAll(promiseArr) {
        let arr = [],
        errorMsg = [],
        resolvedPromiseCount = 0;
    
        return new Promise((resolve, reject) => {
            promiseArr.map((each_promise, index) => {
                return each_promise.then((res) => {
                    console.log(res)
                    arr[index] = res;
                    resolvedPromiseCount++;
    
                    if (resolvedPromiseCount === promiseArr.length) {
                        resolve(arr);
                    }
                })
                .catch((err) => {
                    resolvedPromiseCount++;
                    errorMsg.push(err);
                });
            });
        });
    }
    
    resolveAll([promise1, promise2, promise3]).then((res) => {
        console.log(res, "result");
    })
    .catch((err) => console.log(err, "error"));