The javascript Promise problem that has bothered me all day

First, top level code is executed: enter image description here

Through the execution, two functions are added to the microtask queue (red dots). Then, microtask queue is executed: enter image description here

One function is added to a microtask queue and then the function in the microtask queue is executed:

enter image description here

The End.


// Defining new promise 1
new Promise(resolve => {
    // Defining new promise 2
    new Promise(resolve1 => {
        // print 1
        console.log(1)
        // Resolve promise 1
        resolve1()
    })
        .then(() => {
            // After resolve 1 (then method), print 2
            console.log(2)
        })
        .then(() => {
            // Here we see chain of then methods, so after then 1
            // print 3. But keep in mind, that we also resolves
            // promise 1 before then of promise 2 actually happens,
            // so now JS doing it's own "parallel" tasks
            // (task/microtask pool, event loop)
            console.log(3)
        })
    // Resolve promise 1
    // BEFORE then from promise 2 completed
    resolve()
})
.then(() => {
    // This then method print 4
    console.log(4)
})

You can alter this code and make it async like that, by adding async/await

// Defining new promise 1
new Promise(async (resolve) => {
    // Defining new promise 2
    await new Promise(resolve1 => {
        // print 1
        console.log(1)
        // Resolve promise 1
        resolve1()
    })
        .then(() => {
            // After resolve 1 (then method), print 2
            console.log(2)
        })
        .then(() => {
            // chain then, prints 3 after first then
            console.log(3)
        })
    // Resolve promise 1
    // AFTER promise 2 complete its then
    resolve()
})
.then(() => {
    // This then method print 4
    console.log(4)
})

They are two separate promises and will execute in parallel regardless of the order you put them in. If you're looking to get an order of 1, 2, 3, 4 you have to chain them properly or bring them together with Promise.all e.g.

let outerPromise, innerPromise;

outerPromise = new Promise(resolve => {
    innerPromise = new Promise(resolve1 => {
        console.log(1)
        resolve1()
      })
      .then(() => {
        console.log(2)

      })
      .then(() => {
        console.log(3)
      })
    resolve()
  })
  .then(() => {
    Promise.all([outerPromise, innerPromise]).then(() => console.log(4));
  });

Here is a good resource to read about promise chaining -> https://javascript.info/promise-chaining


The easiest to understand what happens with Promises is to unchain it as much as you can.

So in this case, we have to remember that the Promise constructor is ran synchronously, microtasks are pushed to a queue (FIFO) and that it's only when the promise to which we attached our callback with .then() will resolve, that our callback will get pushed to that queue.

So we can rewrite your snippet like this:

const outer_1 = new Promise(resolve => {
  const inner_1 = new Promise(resolve1 => {
    console.log(1);
    resolve1();
  });
  const inner_2 = inner_1.then(() => {
    console.log(2);
  });
  const inner_3 = inner_2.then(() => {
    console.log(3);
  })
  resolve()
});
const outer_2 = outer_1.then(() => {
  console.log(4)
})
/*
And the execution order:

# sync
  inner_1 ->
    log(1)
    queue microtask (inner2)
  outer_1 ->
    queue microtask (outer2)
# microtask-checkpoint
  inner_2 -> 
    log(2)
    queue microtask (inner3)
  outer_2 ->
    log(4)
  inner_3 ->
    log(3)
*/