Async/Await: Common Mistakes
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. ππΌ