1

I was writing polyfill for Promise.race() method using the below code snippet.

const promise = new Promise((resolve, reject) => {
  resolve('promise resolved');
});

const promise2 = Promise.resolve('promise2 resolved');

function raceCopy(promises) {
  return new Promise(function (resolve, reject) {
    for (var index = 0; index < promises.length; index++) {
        Promise.resolve(promises[index])
          .then(resolve)
          .catch(reject);
    }
  });
}

raceCopy([Promise.reject('copy rejected'), promise, promise2])
  .then((response) => console.log(response))
  .catch((error) => console.error(error));

But instead of giving the error "copy rejected" in the console, it is showing "promise resolved". I do not understand How and Why it is working like this?

What I tried?

  1. I used the existing Promise.race() API, and it is giving me the correct result "copy rejected".
Promise.race([Promise.reject('copy rejected'), promise, promise2])
 .then((response) => console.log(response))
 .catch((error) => console.error(error));
  1. Second, I changed the implementation of the raceCopy(). Something like this,
function raceCopy(promises) {
  return new Promise(function (resolve, reject) {
    for (var index = 0; index < promises.length; index++) {
      Promise.resolve(promises[index]).then(resolve, reject);
    }
  });
}

And It is also giving the correct result.

What I'm not understanding? If all the implementations are the same then Why are the results not the same for all? What am I missing here?

  • 1
    `Promise.resolve` doesn't necesseraly means success. Try running `Promise.resolve(Promise.reject("test")).catch((e) => console.error(e));` and you'll see :) – Drago96 Jun 27 '22 at 08:56
  • 1
    Related: [the difference between `.then(…, …)` and `.then(…).catch(…)`](https://stackoverflow.com/q/24662289/1048572) – Bergi Jun 27 '22 at 09:02

1 Answers1

2

I've done some tests and found interesting results. Please be aware that this is just something I found out doing tests, so it might be not 100% accurate; a deep dive into the Ecmascript documentation would be more correct for sure.

Basically, the reason that your original code doesn't work is because of the chaining of .then and .catch. It is because of how microtasks are queued in this situation. Let's show an example using the following input:

var promises = [Promise.reject('rejected'), Promise.resolve('resolved')];

if in your code you use chaining, this is the result:

Promise.resolve(promises[index])
    .then(resolve)
    .catch(reject)

/*
-----------------------
Microtask queue
-----------------------
- .then of index 0, then enqueue the catch
- .then of index 1 (which resolve the promises)
- .catch of index 0 (which gets ignored)

------------------------------------------------------------
If we reverse the chaining
*/

Promise.resolve(promises[index])
    .catch(reject)
    .then(resolve)

/*
-----------------------
Microtask queue
-----------------------
- .catch of index 0 (which reject the promises) and don't enqueue following .then
- .then of index 1 (which gets ignored)

*/

The first .catch ends up at the end of the microtask queue because it was enqueued in the first microtask, while the other .then are all enqueued in the main task at the very beginning.

This is just speculation, but I think that it works this way to allow for a single .catch to catch errors even if there are multiple .then before it.

The correct way to fix it is probably the one you already found in your last example. By only using one .then you are only queueing one microtask and it will handle it in the correct way.

Drago96
  • 1,265
  • 10
  • 19
  • Yeah! I also think this works somewhat this way. As I changed the `catch` and `then` order in the first code snippet of the above question and now I think, it is queueing all the `catch` first and then `then`. Thanks for answering. – Himanshu singh Jun 30 '22 at 14:47
  • You're welcome :) If you think this answers your question, you can mark this as the answer (click on the icon on the left of the text) – Drago96 Jun 30 '22 at 16:22
  • Hey @Drago96, on changing the order of `then` and `catch` we are getting the result for the above code snippet. But if we will change the position of `Promise.reject()` in the argument we are passing to `raceCopy()` function, something like this `[1st ele, 2nd ele, Promise.reject()]`. Then again we will not get right result. So, I think `.then(cb1, cb2)` is only way to go. – Himanshu singh Aug 16 '22 at 13:35