0

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

mayuri
  • 135
  • 3
  • 14
  • "*I am trying to write a polyfill for Promise.all*" - why? Is it for educational purposes only? Then notice that you're not handling errors, empty arrays, iterables, and thenables correctly either. – Bergi Jul 14 '21 at 17:48

1 Answers1

2

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"));
Shoejep
  • 4,414
  • 4
  • 22
  • 26
  • 1
    @mayuri Then you should accept the answer with the grey tick mark next to it. Btw, if you want to mimic `Promise.all`, you should `reject` immediately when you `catch` an error. – FZs Jul 14 '21 at 17:42
  • 1
    @FZs Thank you, I edited the code, added reject in the catch block. – mayuri Jul 14 '21 at 18:02