The javascript Promise problem that has bothered me all day

Viewed 196

How does this code work?

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

The result is ‘1 2 4 3’

6 Answers

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)
*/

// 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)
})

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.

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

Whenever "resolve ()" is called that completes the promise.

Also JavaScript function calls aren't blocking, meaning when you do .then on something, then another call afterwards, the second call will be executed first, then the first one. Similar idea with setTimeout etc

Here

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

You first declare a new Promise, which inherently doesn't console log anything (except 1)' the logging for that one only happens in the .then calls, which, as mentioned, are executed after (some) regular function call that follows then You are then following it with the main resolve, which prints 4. In the meantime, though, console.log takes time to operate, so even though the 2 was console logged, but it didn't have enough time to log 3 before 4, which is not dependant on waiting on the promise chain, was printed

Questions?

Because you're calling resolve() before the third .then() can be called.

It executes like this...

new Promise(resolve => {
    new Promise(resolve1 => { // okay. we're not waiting. let's keep moving
        console.log(1)
        resolve1(); // oh, yay! let's call .then()
      })
      .then(() => {
        console.log(2) // nothing was returned! let's call .then()
      })
      .then(() => {
        console.log(3) // nothing was returned! let's call .then()
      });
    // this is the next thing to be called after new Promise(resolve1 ...)
    // it will be called almost at the exact same time that resolve1() is called
    resolve(); oh, yay! let's call .then()
  })
  .then(() => {
    console.log(4)
  })
Related