Search code examples
javascriptasynchronouspromisetask-queueorder-of-execution

Execution order question when processing JavaScript asynchronous functions (promise)


I'd like to know the order in which the JavaScript code below is executed and why. In particular, I would like to learn more about the contents of the code that goes into the microtask queue, focusing on step-by-step

var promise = Promise.resolve();

promise = promise.then(function(rtnVal) {
  console.log('1');
  var promise2 = Promise.resolve();
  promise2 = promise2.then(function(rtnVal) {
    console.log('2');
  });
  promise2 = promise2.then(function(rtnVal) {
    console.log('3');
  });
  console.log('4');
});

promise = promise.then(function(rtnVal) {
  console.log('5');
  var promise2 = Promise.resolve();
  promise2 = promise2.then(function(rtnVal) {
    console.log('6');
  });
  promise2 = promise2.then(function(rtnVal) {
    console.log('7');
  });
  console.log('8');
});

console.log('9');

Here's what I did: 9,1,4,2,5,8,3,6,7 ->I'm wondering why it runs in this order`


Solution

  • Some things to be aware of:

    • When a promise is resolved, any then callbacks (if any) are put on the PromiseJob queue
    • When then() is called (not its callback), the returned promise is always pending, even if that then() call is made on a resolved promise. This is because the returned promise can only be resolved by executing the callback that is given to the then method since it determines how the promise will resolve, but which can only execute asynchronously.

    To ease the analysis, I modified your script to give a name to each promise (a to i) and to each callback function (a_then, ...), but the logic and output remains the same:

    var a = Promise.resolve();
    
    var b = a.then(function a_then() {
      console.log(1);
      var c = Promise.resolve();
      var d = c.then(function c_then() {
        console.log(2);
      });
      var e = d.then(function d_then() {
        console.log(3);
      });
      console.log(4);
    });
    
    var f = b.then(function b_then() {
      console.log(5);
      var g = Promise.resolve();
      var h = g.then(function g_then() {
        console.log(6);
      });
      var i = h.then(function h_then() {
        console.log(7);
      });
      console.log(8);
    });
    
    console.log(9); 

    Here is a sequence of events that happen during the execution of that code.

    • The first column represents what is executing (the main script, a function initiated from the event loop, or the host that dequeues an item from the PromiseJob queue)
    • The second column has the current expression/statement being evaluated
    • The columns a to i represent the state of the promise with that name: ? for pending, F for fulfilled.
    • The last column pictures what is present in the PromiseJob queue, managed by the host

    Here we go:

    Task Action a b c d e f g h i PromiseJob queue
    Script a = Promise.resolve() F - - - - - - - -
    Script b = a.then(a_then) F ? - - - - - - - a_then
    Script f = b.then(b_then) F ? - - - ? - - - a_then
    Script console.log(9) F ? - - - ? - - - a_then
    Host dequeue a_then F ? - - - ? - - -
    a_then console.log(1) F ? - - - ? - - -
    a_then c = Promise.resolve() F ? F - - ? - - -
    a_then d = c.then(c_then) F ? F ? - ? - - - c_then
    a_then e = d.then(d_then) F ? F ? ? ? - - - c_then
    a_then console.log(4) F ? F ? ? ? - - - c_then
    a_then return resolves b F F F ? ? ? - - - c_then, b_then
    Host dequeue c_then F F F ? ? ? - - - b_then
    c_then console.log(2) F F F ? ? ? - - - b_then
    c_then return resolves d F F F F ? ? - - - b_then, d_then
    Host dequeue b_then F F F F ? ? - - - d_then
    b_then console.log(5) F F F F ? ? - - - d_then
    b_then g = Promise.resolve() F F F F ? ? F - - d_then
    b_then h = g.then(g_then) F F F F ? ? F ? - d_then, g_then
    b_then i = h.then(h_then) F F F F ? ? F ? ? d_then, g_then
    b_then console.log(8) F F F F ? ? F ? ? d_then, g_then
    b_then return resolves f F F F F ? F F ? ? d_then, g_then
    Host dequeue d_then F F F F ? F F ? ? g_then
    d_then console.log(3) F F F F ? F F ? ? g_then
    d_then return resolves e F F F F F F F ? ? g_then
    Host dequeue g_then F F F F F F F ? ?
    g_then console.log(6) F F F F F F F ? ?
    g_then return resolves h F F F F F F F F ? h.then
    Host dequeue h_then F F F F F F F F ?
    h_then console.log(7) F F F F F F F F ?
    h_then return resolves i F F F F F F F F F
    Host queue is empty F F F F F F F F F