javascriptnode.jsasynchronousasync-await

Promise vs async/await - is it really just syntactic sugar?


It's often said that async/await is just syntactic sugar over promises, and we can re-write code using one approach to use the other.

Let's say I have this piece of code using promises:

async function main() {
    const promise = new Promise((resolve) => {
        return resolve('promise resolved value')
    })

    promise.then((res) => console.log(res))
    console.log('after promise.then()')
}

main().then((res) => console.log('end'))

The output would be:

after promise.then()
promise resolved value
end

which makes sense. Now the issue I have is if I re-write this code using async/await:

async function main() {
    const asyncFunc = async () => {
        return 'async func resolved value'
    }
    console.log(await asyncFunc())
    console.log('after await asyncFunc()')
}

main().then((res) => console.log('end'))

The order of execution changes:

async func resolved value
after await asyncFunc()
end

This is confusing to me. How should I rewrite the code using async/await so I would get the same order of execution?


Solution

  • First some comments on your scenario set-up:

    Now to making it output in the same sequence with async/await, you could place the code that waits for promise to resolve in its own async function, so that you can perform the 'after then()' output outside of it (ensuring it is independent from a promise, like in the first snippet)

    So I adapted the original code to not use async at all:

    // To make the exercise fair, don't use ASYNC keyword here:
    function main() {
        // Create an immediately resolved promise this way:
        const promise = Promise.resolve('promise resolved value');
        promise.then((res) => console.log(res));
        console.log('after promise.then()');
    }
    
    // ...and create the "outside" promise here:
    Promise.resolve(main()).then((res) => console.log('end'));

    And the following snippet could be a translation of that to async/await, where the only Promise#then call is in the top-level code:

    async function main() {
        // In the original code, the promise was created on the spot, so also here
        const promise = (async () => {
            return 'promise resolved value';
        })(); // ... immediately execute the async function to get the promise
    
        // Isolate the code that is promise-dependent
        (async () => {
            const res = await promise;
            console.log(res);
        })(); // and execute it without waiting for the promise it returns
    
        // Execute this synchronously:
        console.log('after then()');
    }
    
    main().then((res) => console.log('end'));

    As you can see, this does not make the code any more elegant or more readable. You should use the syntax that best suits the purpose.

    And there really is no good reason to avoid the use of Promise.resolve when creating an immediately resolved promise. Using async syntax for that is making your code more verbose and hard-to-read than needed.