skip to Main Content

Its often said that async/await is just syntactic sugar over promises, and we can re-write code using one approach over other. Lets 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 change:

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?

2

Answers


  1. First some comments on your scenario set-up:

    • The first code snippet has already an async keyword, which makes the conversion to async/await syntax a bit blurred — you didn’t start from a scenario where you didn’t use it.

    • There seems no good reason why the original main function should be an async function as it doesn’t execute an await. All of its code (so excluding callbacks) executes synchronously. main returns an immediately resolved promise. The only purpose that serves, is that you delay the execution of console.log('end'), but for that you could have done (without async):

      Promise.resolve(main()).then((res) => console.log('end'));
      
    • The immediately resolving promise called promise can also be created in a more simple way, using Promise.resolve('promise resolved value')

    • The output "after promise.then()" is generated synchronously, independent of any promise. If you want to translate that to an async/await version, it should also occur at a place where it is independent of any promise, and therefore should certainly not be placed after an await operation.

    • Not a big issue, but in the second snippet you have not created the promise in an assignment like in the first snippet. To align it more with the first snippet, you would immediately execute the async function and assign that promise to promise.

    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.

    Login or Signup to reply.
  2. These pieces of code aren’t equivalent. The snippet with raw promises contains problems:

    • it uses async without a reason
    • main contains detached promise that isn’t returned, which is a bad practice
    • there’s no reason to place console.log after promise.then(...).

    This is how it would be written with raw promises:

    function main() {
        const asyncFunc = () => Promise.resolve('value')
        console.log('before then')
        return asyncFunc().then(res => console.log(res))
    }
    
    main().then(...)
    

    And fully equivalent async..await:

    async function main() {
        const asyncFunc = async () => 'value';
        console.log('before then')
        const res = await asyncFunc();
        console.log(res);
    }
    
    await main();
    ...
    

    Notice that a promise doesn’t exist in both cases before 'before then'. In some cases it may be necessary to create a promise, do a synchronous operation and then wait for a promise, like shown in the original code with raw promises. In this case it would be changed to:

    async function main() {
        const asyncFunc = async () => 'value';
        const promise = asyncFunc();
        console.log('before then')
        await promise;
        console.log(res);
    }
    
    await main();
    ...
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search