Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding the execution order of subsequent then() handlers of an resolved promise

I am learning Promise, in order to understand it I read a bit about Event loop of JavaScript. This article briefly introduced the working of event loop such as call stack, event table and message queue.

But I don't know how the call stack deal with the line containing 'return', and what happens thereafter. Below is an example that I wrote to hopefully understand how Promise works based on event loop. Also see http://jsbin.com/puqogulani/edit?js,console if you want to give it a go.

var p1 = new Promise(
  function(resolve, reject){
    resolve(0);
});

p1.then(function(val){
  console.log(val);
  p1.then(function(){
    console.log("1.1.1");
    p1.then(function(){
      console.log("1.1.2");
      p1.then(function(){
        console.log("1.1.3");
      });
    });
  });

  p1.then(function(){
    console.log("1.2");
  })

  return 30;

  //return new Promise(function(resolve, reject){
  //  resolve(30);
  //});

})
  .then(function(val){
  console.log(val/2);
});

p1.then(function(){
  console.log("2.1");
});

console.log("Start");

As can be seen, there are two "return", using each of them will give a different output order. Specifically, when using return 30;, 1.1.2, 1.1.3 are after 15, but when using return new Promise(...), 1.1.2, 1.1.3 are before 15. So what exactly happened when the code reached two different 'return'?

like image 809
Junlong Wang Avatar asked Jan 22 '17 07:01

Junlong Wang


People also ask

How are promises executed in JavaScript?

Just to review, a promise can be created with the constructor syntax, like this: let promise = new Promise(function(resolve, reject) { // Code to execute }); The constructor function takes a function as an argument. This function is called the executor function .

Does Promise all resolve in order?

Promise. all is actually a promise that takes an array of promises as an input (an iterable). Then it gets resolved when all the promises get resolved or any one of them gets rejected.

What is then in Promise?

The then method returns a Promise which allows for method chaining. If the function passed as handler to then returns a Promise , an equivalent Promise will be exposed to the subsequent then in the method chain. The below snippet simulates asynchronous code with the setTimeout function. Promise.


1 Answers

The difference is described in http://promisesaplus.com/ under the promise resolution procedure.

For the first return value:

2.3.3.4 If then is not a function, fulfill promise with x.

For the second:

2.3.2 If x is a promise, adopt its state [3.4]:

2.3.2.2 If/when x is fulfilled, fulfill promise with the same value.

We can see this implemented q.js. This is one possible implementation but seems to explain the delay:

function coerce(promise) {
    var deferred = defer();
    Q.nextTick(function () {
        try {
            promise.then(deferred.resolve, deferred.reject, deferred.notify);
        } catch (exception) {
            deferred.reject(exception);
        }
    });
    return deferred.promise;
}

When returning a promise from the then function, we have two separate promise objects: the one returned from the function passed to then, and the one returned from then. These need to be connected together so that resolving the first, resolves the second. This is done with promise.then(deferred.resolve, ...)

The first delay comes from Q.nextTick. This executes the function on the next iteration of the event loop. There's some discussion in the commit comments on why it's needed.

Calling promise.then adds a further delay of one iteration of the event loop. As required in the spec:

2.2.4 onFulfilled or onRejected must not be called until the execution context stack contains only platform code. [3.1].

The execution would go something like:

p1.then with function containing 1.1.1 is called
    function containing 1.1.1 is queued
p1.then with function containing 1.2 is called
    function containing 1.2 is queued
Promise resolving to 30 is returned
    Q.nextTick function is queued
----------
1.1.1 is printed
p1.then with function containing 1.1.2 is called
    function containing 1.1.2 is queued
1.2 is printed
Q.nextTick function is executed
    promise.then(deferred.resolve, ...) is queued
----------
1.1.2 is printed
p1.then with function containing 1.1.3 is called
    function containing 1.1.3 is queued
promise.then(deferred.resolve, ...) is executed
    function containing val/2 is queued
----------
1.1.3 is printed
val/2 is printed
like image 104
fgb Avatar answered Oct 06 '22 01:10

fgb