-1

I wonder why the execution order is "0 1 2 3 4 5 6" instead of "0 1 4 2 3 5 6".

Promise.resolve().then(() => {
  console.log(0)
  return Promise.resolve(4)
}).then((res) => {
  console.log(res)
})

Promise.resolve().then(() => {
  console.log(1)
}).then(() => {
  console.log(2)
}).then(() => {
  console.log(3)
}).then(() => {
  console.log(5)
}).then(() => {
  console.log(6)
})
Kaiido
  • 123,334
  • 13
  • 219
  • 285
Dylan Zhang
  • 177
  • 1
  • 9
  • Does this answer your question? [What happen when we return a value and when we return a Promise.resolve from a then() chain, in the microtask queue?](https://stackoverflow.com/questions/60912300/what-happen-when-we-return-a-value-and-when-we-return-a-promise-resolve-from-a-t) – Kaiido Oct 11 '22 at 05:46
  • 2
    Actually https://stackoverflow.com/questions/46408228/es6-promise-execution-order-for-returned-values is a better target. – Kaiido Oct 11 '22 at 05:47
  • I'm still wondering why does it take 2 microtasks just to unwrap the returned promise -- I would've thought it takes only one. On my way to figure it out... – FZs Oct 11 '22 at 07:05
  • 1
    Why does it matter ? You cant count micro tasks and rely on that to sync your program anyway. – Clem Oct 11 '22 at 08:31
  • In the end, these kinds of questions are actually not useful in real world coding because you should never be coding things to depend upon a specific number of micro task ticks. If you need A to happen before B, then code it to happen that way, regardless of the details of micro task ticks. Plus this stuff occasionally changes as implementations get modified and specs change, often in the name of performance optimizations. Don't ever write code that depends upon this level of implementation detail. – jfriend00 Oct 11 '22 at 08:46

1 Answers1

1

It happens that way because it takes the JavaScript interpreter multiple microtasks to unwrap the value (4) from a returned promise.

That's because promises are built in a way that most of the time only one thing happens in each microtask. A notable example is that then callbacks always run in a new microtask.

The second chain of promises is executed alternately with the first chain. Its presence doesn't change the result, but it highlights how many steps (microtasks) it takes for something to happen.

In this case, the following happens (disregaring the second chain of promises here):

  • The then callback logging 0 runs, and returns the promise resolved to 4.
  • The then method of the returned promise is called internally to extract its value.
  • The then callback is called and receives the value 4. The first promise is now fulfilled, it schedules its then callback to be executed.
  • The last then callback is called, receiving and logging the value 4.

So, in total, it takes 4 microtasks to print the value 4, which matches what we see.

You can observe this behavior by returning a custom thenable object, that forwards its operations to a real promise (I changed the logged values here to better illustrate what's going on):

Promise.resolve().then(() => {
  console.log('#1')
}).then(() => {
  console.log('#2')
}).then(() => {
  console.log('#3')
}).then(() => {
  console.log('#4')
}).then(() => {
  console.log('#5')
}).then(() => {
  console.log('#6')
})

Promise.resolve().then(() => {
  console.log(1)

  const returnValue = Promise.resolve(4)

  return {
    then(rs, rj){
      // This is "inside" `Promise.resolve(4).then()`
      console.log(2)
      returnValue.then(v => {
        //This is "inside" the callback given by the interpreter 
        console.log(3)
        rs(v)
      })
    }
  }
}).then((res) => {
  console.log(4)
})
FZs
  • 16,581
  • 13
  • 41
  • 50
  • I also offer a Github link https://github.com/domenic/promises-unwrapping/blob/master/reference-implementation/lib/testable-implementation.js to help us understand how the Promise works and when to unwrap a promise. – Dylan Zhang Nov 09 '22 at 17:37