Search code examples
javascriptecmascript-6promisejestjses6-promise

How to determine es6-promise constructor execution order?


Consider the following code.

(async () => {
    const values = {}
    const orders = []
    let promise = 0
    promise = new Promise(async resolve => {
        values[1] = promise
        orders.push(1)
        resolve(1)
        values[2] = promise
        orders.push(2)
        await new Promise(async r => {
            values[3] = await promise
            orders.push(3)
            setTimeout(r, 100)
        })
        values[4] = await promise
        orders.push(4)
        promise = 2
        values[5] = promise
        orders.push(5)
    })
    values[6] = await promise
    orders.push(6)
    await new Promise(resolve => setTimeout(resolve, 100)) // (*). try removing this line, result will change
    values[7] = await promise
    orders.push(7)
    console.log(values, orders)
})()

Console output:

{ '1': 0, '2': 0, '3': 0, '4': 1, '5': 2, '6': 1, '7': 1 }
[ 1, 2, 3, 6, 4, 5, 7 ]

My questions are:

  1. Is there any statement in the above snippet which is undefined behavior? If no, I am confused of the ES6 promise after checking the result.

  2. Why is values[7] 1, not 2? Update: with node v10.19.0, it is 1. With nodev14.0.0, node-v12.10.0 and browser, it is 2.

Update 2: repeating the evaluation with node v10.19.0, after 20 times of values[7] be 1, once it will be 2.

  1. If I remove the (*) line, the result changes to below. Why are 4, 5 removed?

    { '1': 0, '2': 0, '3': 0, '6': 1, '7': 1 } [ 1, 2, 3, 6, 7 ]

If you have any rule of thumb for this kind of execution, please share. The more I write JS promise code, the more I get confused.

Check for more tests here.


Solution

  • If there's something you're confused about, try to distill it to just the case you are confused about, not all the other cases too, that said... my best guess to what I think you want to know:

    1. The executor function (what you pass to new Promise()) gets called immediately, synchronously.
    2. Async functions, when called, also get executed immediately. They will only pause when they hit the first await.
    3. So if you use an async function that has no await in it, the entire async function will get executed synchronously, immediately.
    4. However, even if you async function is "synchronous", it will not immediately return. Execution is synchronous, but resolving (handling .then() listeners) will be in the next tick).