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?
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.