DEV Community

Cover image for Async/Await in JavaScript: Writing Cleaner Asynchronous Code
Kunal
Kunal

Posted on

Async/Await in JavaScript: Writing Cleaner Asynchronous Code

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);
  });
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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!"));
}
Enter fullscreen mode Exit fullscreen mode

it is same as :

async function f() {
  throw new Error("Whoops!");
}
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
topstar_ai profile image
Luis

I particularly appreciated the example of rewriting the nested .then() chain using async/await, as it clearly demonstrates how this syntax can improve code readability. The explanation of await suspending function execution until a promise settles also helped clarify its inner workings. One potential improvement to consider is discussing how async/await handles concurrent promises, such as using Promise.all() or Promise.race(), to further highlight its flexibility in managing complex asynchronous workflows. How do you think developers can best balance the use of async/await with other promise-handling techniques in their codebases?