Using async and await every day but never really understood why
Most of us use async and await every day.
But have you ever stopped and asked
Why do we even need them
In this blog we will understand it in the simplest way possible.
What we will learn
- Why async and await were introduced
- How async functions work
- What the await keyword actually does
- How to handle errors
- Async await vs promises
Why async and await were introduced
Before async and await came in ES2017 developers used Promises with .then() and .catch().
It worked perfectly.
But the code started becoming harder to read as it grew.
For example
fetch("/user")
.then((res) => res.json())
.then((user) => {
fetch(`/posts/${user.id}`)
.then((res) => res.json())
.then((posts) => {
fetch(`/comments/${posts[0].id}`)
.then((res) => res.json())
.then((comments) => {
console.log(comments);
});
});
})
.catch((error) => {
console.log(error);
});
As more .then() blocks get nested the code becomes harder to read and maintain.
We can write that code as :
async function getData() {
try {
const userResponse = await fetch("/user");
const user = await userResponse.json();
const postsResponse = await fetch(`/posts/${user.id}`);
const posts = await postsResponse.json();
const commentsResponse = await fetch(`/comments/${posts[0].id}`);
const comments = await commentsResponse.json();
console.log(comments);
} catch (error) {
console.log(error);
}
}
getData();
That is why JavaScript introduced async and await.
It make our code much readable and maintainable
But why we need that async keyword everytime
How async Functions Work
As we have seen, async is a keyword placed before a function that makes the function always return a Promise, regardless of what you return inside it.
Promises solved the problem of callback hell, but nested .then() and .catch() can still make code harder to read and maintain.
async/await is syntactic sugar built on top of Promises. It was introduce to make asynchronous code look and behave a little more like synchronous code.
We could explicitly return a promise, which would be the same:
async function f() {
return Promise.resolve(1);
}
f().then(alert); // 1
so async ensure that function returns a promise and wraps a non-promise in it but not only that another keyword await that is also important
What the await keyword actually does
await is the keyword that wrap inside the async function it makes the javascript wait until the promise settles and returns the result.
Here’s an example with a promise that resolves in 1 second:
async function f() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("Like this blog!"), 1000)
});
let result = await promise; // wait until the promise resolves (*)
alert(result); // "done!"
}
f();
You can't use await without the async keyword otherwise your compiler show syntax error
But how is it making javascript wait ??
Await suspend the function execution until the promise settles and the resume it with the promise result . this makes the program efficient as it doesnot cost any resources to CPU as javascript engine can do another jobs in the meantime
How to handle errors
We have now understand how the async and await works together but what if we want to handle exception in async/await the concept remains the same as normal exception handling
we do :
async function f() {
await Promise.reject(new Error("Whoops!"));
}
it is same as :
async function f() {
throw new Error("Whoops!");
}
But we dont use that promise method as it can rise some delay issue instead we use try/catch block
async function f() {
try {
let response = await fetch('http://no-such-url');
} catch(err) {
alert(err); // TypeError: failed to fetch
}
}
f();
it works really well if you want to know more about this i have one dedicated blog over try/catch you can read that too .
Async await vs promises
Promises (.then() / .catch()) |
Async/Await |
|---|---|
Uses .then() and .catch() to handle async code. |
Uses async and await keywords. |
Can become hard to read with multiple .then() calls. |
Looks like normal synchronous code, so it's easier to read. |
| Chaining many async operations can get messy. | Multiple async operations are easier to write step by step. |
Error handling is usually done with .catch(). |
Error handling is usually done with try...catch. |
| Good for simple promise chains. | Better for most real-world async code because it's cleaner and easier to maintain. |
Thanks for reading ! if enjoyed this blog , you can read more on this 👇
Top comments (1)
I particularly appreciated the example of rewriting the nested
.then()chain usingasync/await, as it clearly demonstrates how this syntax can improve code readability. The explanation ofawaitsuspending function execution until a promise settles also helped clarify its inner workings. One potential improvement to consider is discussing howasync/awaithandles concurrent promises, such as usingPromise.all()orPromise.race(), to further highlight its flexibility in managing complex asynchronous workflows. How do you think developers can best balance the use ofasync/awaitwith other promise-handling techniques in their codebases?