Search code examples
javascriptasync-awaitpromise

Why `after resolve` is executed before than `after wait`?


the code snippet likes below:

console.log('test')

async function async1() {
    console.log('before wait');
    await wait();
    console.log('after wait');
}

async function wait() {
    try {
        return await new Promise((res, rej) => {
            console.log('wait');
            rej('reject');
        });
    } catch (reason) {
        console.log(reason);
    }
}

async function test() {
    async1();
    console.log('before resolve');
    await Promise.resolve();
    console.log('after resolve');
}

test();

console.log('test end');

you can also run the code in playground

actual result:

[LOG]: "test" 
[LOG]: "before wait" 
[LOG]: "wait" 
[LOG]: "before resolve" 
[LOG]: "test end" 
[LOG]: "reject" 
[LOG]: "after resolve" 
[LOG]: "after wait" 

I thought after wait would be printed first, since it is pushed in the micro task queue early?

where am I wrong?


I took advice from @Seblor

It may become clearer if you replace the async/await with the then function.

then I removed all the async/await

console.log('test')

function async1() {
    console.log('before wait');
    return wait().then(() => {
        console.log('after wait');
    })
}

function wait() {
    return new Promise((res, rej) => {
        console.log('wait');
        rej('reject');
    }).catch((reason) => {
        console.log(reason);
    })
}

function test() {
    async1();
    console.log('before resolve');
    Promise.resolve().then(() => {
        console.log('after resolve');
    })
}

test();

console.log('test end');

Then I realized the first code pushed in micro task queue is the code in catch , followed by console.log('after resolve'), finally console.log('after wait')


Solution

  • Okay I had a hard time spotting it but I think I get it.

    So we all agree where the issue is, the problem you see is that the promise rejection is executed before the promise that resolves, but the 'after resolve' is printed before 'after wait'.

    The thing is that even though there are 2 promises, there are 3 micro tasks here. The first one is the return await new Promise(, the second one is await Promise.resolve(), and the 3rd one is the awaiting of the call to the function: await wait().

    The call to wait() returns a promise, which you await, creating a new microtask that is queued AFTER await Promise.resolve().

    So the rejecting promise finished, printing 'reject', then the resolving promise finished, printing 'resolve', and finally the returned promise from wait() is awaited, printing 'after wait'.

    It may become clearer if you replace the async/await with the then function. So this:

    async function async1() {
        console.log('before wait');
        await wait();
        console.log('after wait');
    }
    

    Would be written like this:

    function async1() {
        console.log('before wait');
        return wait().then(() => {
            console.log('after wait');
        });
    }
    

    So in order of execution (of the micro tasks only):

    1. await new Promise((res, rej) => { registers the 1st Promise
    2. await Promise.resolve() registers the 2nd Promise
    3. The 1st Promise is rejected, printing 'reject'
    4. Since the 1st promise returns, we can finally register the then callback, creating the 3rd microtask.
    5. The 2nd Promise is resolved, printing 'after resolve' since that's synchronous code
    6. The 3rd micro task is executed, printing 'after wait'