What await cannot do
Before we get too comfortable using await
in our code we need to realize that we cannot:
- Use
await
in a function that is not markedasync
. You have to prefix the function withasync
keyword if you are going to useawait
inside it. - Use
await
on the top level.
We discussed the first item in previous blog post. For the second one here is an example:
async function wait(message, time) {
return new Promise((resolve) => setTimeout(resolve(message), time));
}
await wait ("hello", 2000); // SyntaxError: await is only allows inside an async function
We can rewrite this to make it work correctly.
async function wait(message, time) {
return new Promise((resolve) => setTimeout(resolve(message), time));
}
async function execute() {
const message = await wait ("hello", 2000);
console.log(message); // hello (after 2000 ms)
}
execute();
🚨 Top-level await proposal is here and V8 already supports it
Accidentally making code synchronous
The biggest issue with async/await is the await
keyword and how it is easy to misuse it. We almost always want our code to run asynchronously (if we have the option) and make sure we do not block the client.
To help us understand this, let's start with a promise example, convert it into async/await and then correct a mistake that happens far too often.
const sayGreeting = (name, time) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(`Hello ${name}`);
}, time);
})
}
sayGreeting("Parwinder", 1000)
.then((data) => {
console.log(data); // "Hello Parwinder" after 1 second
return sayGreeting("Lauren", 2000);
})
.then((data) => {
console.log(data); // "Hello Lauren" after 2 seconds
return sayGreeting("Robert", 500);
})
.then((data) => {
console.log(data); // "Hello Robert" after half a second
return sayGreeting("Eliu", 2000);
})
.then((data) => {
console.log(data); // "Hello Eliu" after 2 seconds
return sayGreeting("George", 1500);
})
.then((data) => {
console.log(data); // "Hello George" after 1.5 seconds
})
The above example says a greeting to a person after a specified time. Promises made the code flat as compared to callbacks, but this is still chained code with at least one callback in each link.
An individual that has recently learned await
might rewrite this like so:
const sayGreeting = (name, time) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(`Hello ${name}`);
}, time);
})
}
const main = async () => {
let a = await sayGreeting("Parwinder", 1000);
console.log(a); // "Hello Parwinder" after 1 second
a = await sayGreeting("Lauren", 2000);
console.log(a); // "Hello Lauren" after 2 seconds
a = await sayGreeting("Robert", 500);
console.log(a); // "Hello Robert" after half a second
a = await sayGreeting("Eliu", 2000);
console.log(a); // "Hello Eliu" after 2 seconds
a = await sayGreeting("George", 1500);
console.log(a); // "Hello George" after 1.5 seconds
}
main();
No more then
callbacks and a lot easier to read. So far, we have created a promise and converted it into async/await. The converted code looks a lot better, so where is the mistake?
More often than not, we can do async operations in parallel. Every time I write an await
statement in the main
I am making JavaScript wait for that promise to complete and then move forward. We could probably execute all five promises at the same time and get back the greetings.
The first promise example I provided is chained/sync, as well. So if you have read my previous blog posts on promises, you would know how we run multiple promises at the same time! We use Promise.all
and that is what we are going to do with the async/await example to make it performant.
const sayGreeting = (name, time) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(`Hello ${name}`);
}, time);
})
}
const main = async () => {
const a = sayGreeting("Parwinder", 1000);
const b = sayGreeting("Lauren", 2000);
const c = sayGreeting("Robert", 500);
const d = sayGreeting("Eliu", 2000);
const e = sayGreeting("George", 1500);
const [greeting1, greeting2, greeting3, greeting4, greeting5] = await Promise.all([a, b, c, d, e]);
// all promises in promise.all
console.log(greeting1, greeting2, greeting3, greeting4, greeting5)
}
main();
What did we do here:
- Instead of awaiting for each promise, we stored the promise in a variable.
- Created a mega promise that has
all
promises passed to it. - We
await
thisPromise.all
instead of individual promises. -
Promise.all
executes all promises at the same time and when all of them finish, assigns the response to variables - We log the results 🙂
I hope this improves your ability to use async/await. We will learn about error handling with async/await in the next blog post.
Until then, happy coding. 👋🏼
Top comments (2)
I think the article requires some kind of clarification, otherwise it is misleading, especially for beginners in asynchronous programming in JavaScript. The first rewrite of the Promise chain with multiple await statements is the correct way to go, since it's clear from the original structure that each call to sayGreeting must complete in order for the next call to proceed.
"Every time I write an await statement in the main I am making JavaScript wait for that promise to complete and then move forward."
Indeed, that's exactly what we must do, in order to replicate the original Promise chain and get exactly the same behaviour.
The final rewrite, with the Promise.all does not reflect an exact replication of the original Promise chain and thus misleads beginners into believing that this refactoring will result in exactly the same behaviour.
The clarification, must mention that it was not the intention of the author of the original Promise chain to make each call to sayGreeting dependent on the previous call. But, that would lead us into providing an alternative syntax (without async/await) that uses the Promise.all to make sure that all calls to sayGreeting are executed in parallel.
If the original intention was to run the chain sequentially, then the first rewrite is correct. If the original intention was to run the chain in parallel, then the problem lies in the original syntax.
Very nicely explained 😍😍..
This gave me more clear understanding then before thanks.