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?
Promise.race([Promise.reject('copy rejected'), promise, promise2])
.then((response) => console.log(response))
.catch((error) => console.error(error));
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?
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.