Abhijet
Abhijet

Reputation: 165

Using Promise inside loop

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

Answers (1)

T.J. Crowder
T.J. Crowder

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:

  • The executor function of a promise (the function you pass the constructor) is run synchronously, so there's no difference between having the 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

Related Questions