Reputation: 165
I want to understand how this code works. I have worked around with promise for some time but I am wondering what is going on here.
for(let i=0 ; i< 3 ;i++){
new Promise((resolve,reject)=>{
console.log("i = "+i)
resolve(i)
})
.then(r=>{
console.log("promise 1 of "+r)
return new Promise((res,rej)=>res(r))
})
.then(r=>{
console.log("promise 2 of "+r)
return new Promise((res,rej)=>res(r))
})
}
console.log("finish")
The output is
i = 0
i = 1
i = 2
finish
promise 1 of 0
promise 1 of 1
promise 1 of 2
promise 2 of 0
promise 2 of 1
promise 2 of 2
Why is finish being displayed before the other promises left to execute.
Thanks in advance
Upvotes: 1
Views: 65
Reputation: 1074028
Why is finish being displayed before the other promises left to execute.
Because nothing in that code tells that console.log
to wait for the promises to complete before running. If you want to do that, gather them up in an array, then use:
Promise.all(theArray)
.then(() => {
console.log("finish");
});
...to tell it to wait for them all.
Remember that then
handlers are always called asynchronously. So your code creates three promise chains, then outputs the console.log
at the end, and then gets the asynchronous callbacks to the then
handlers — so those results show up afterward.
Side note: The code in your question can more simply be written like this:
for(let i = 0; i < 3; i++) {
console.log("i = " + i);
Promise.resolve(i)
.then(r => {
console.log("promise 1 of " + r);
return r;
})
.then(r => {
console.log("promise 2 of " + r);
return r;
});
}
console.log("finish");
Why:
console.log("i = " + i);
line as the first line of the executor function, or just above the line where you create the promise.Promise.resolve
creates a resolved promise with the value you provide it.then
always creates a new promise. When you return a non-promise value from the then
handler, that value is the resolution value of the promise then
returns. If you return a promise, then the promise then
created is "slaved" to teh promise you return (waits for it to resolve or reject, and then does the same).In a comment you've asked the very smart question:
If "then" is called asynchonously then the output order of these promises may not be predicted right?
Minor point: then
isn't called asynchronously, its handler (the function you pass it) is called asynchronously.
In the general case, yes, that's right, you can't predict the order of independent promises (ones that aren't chained together). You can predict promises that are chained together (earlier ones in the chain are processed before later ones). In your code, you have three independent chains (one for i = 0, one for i = 1, and another for i = 2); each chain has two handlers attached to it ("1 of" and "2 of"). So you know "1 of" in chain "i = 0" will happen before "2 of" in that same chain, but in the general case you don't know whether the i = 0
one would be first or the i = 2
one or whatever.
But, in this specific case, the order can be predicted (in browsers at least, and also on Node.js) because the initial promise in each chain starts out pre-resolved, which means the call to its then
handler is put in the microtask queue¹ immediately when you call then
. The microtask queue is run at the end of each task (aka "macrotask"). So the task that runs your main code immediately queues the callback to the "1 of" handler of each chain, in order. When that handler is run, it queues a microtask to call the "2 of" handler. So since the promises start out resolved, we know we'll get the output you're getting. But in the general case, you're correct, you can't predict between chains.
¹ That's the web term for it; the JS spec calls it the PromiseJobs queue. More on "microtasks" and tasks/macrotasks in this answer.
Upvotes: 4