Skip to main content

Command Palette

Search for a command to run...

Async/Await in JavaScript: Writing Cleaner Asynchronous Code

Updated
•4 min read•View as Markdown

Introduction

In the previous blog, we learned about promises and how they solve the problem of callback hell.

However, even promises can sometimes look complex when chaining multiple .then() calls.

To make asynchronous code even more readable and easier to write, JavaScript introduced:

Async/Await

Async/await allows us to write asynchronous code in a way that looks almost like synchronous code.


Why Async/Await Was Introduced

Promises improved callbacks, but they still have some drawbacks:

doTask()
  .then((res) => {
    return doNext(res);
  })
  .then((res) => {
    return doAnother(res);
  })
  .then((final) => {
    console.log(final);
  })
  .catch((err) => {
    console.log(err);
  });

Problems:

  • Still looks complex

  • Harder to read for beginners

  • Multiple .then() chains

Async/await was introduced to:

  • Improve readability

  • Make code look synchronous

  • Simplify error handling


What is Async/Await?

Async/await is:

A syntactic sugar over promises that makes asynchronous code easier to read and write.


How Async Functions Work

An async function:

  • Always returns a promise

  • Can use the await keyword inside it

Example:

async function greet() {
  return "Hello";
}

greet().then(console.log);

Output:

Hello

Even though we returned a string, it is wrapped inside a promise.


The Await Keyword

The await keyword:

Pauses the execution of an async function until the promise is resolved.

Example:

function fetchData() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("Data received");
    }, 2000);
  });
}

async function getData() {
  console.log("Start");

  const data = await fetchData();

  console.log(data);
  console.log("End");
}

getData();

Output:

Start
Data received
End

Explanation:

  • await waits for the promise to resolve

  • Code looks synchronous but is asynchronous


Real-Life Analogy

Imagine you are cooking:

  • You start boiling water

  • You wait until it boils

  • Then you continue cooking

This "waiting" is like await, but without blocking the entire system.


Error Handling with Async/Await

With promises, we use .catch().

With async/await, we use try...catch.

Example:

async function fetchData() {
  return new Promise((resolve, reject) => {
    let success = false;

    if (success) {
      resolve("Data received");
    } else {
      reject("Error occurred");
    }
  });
}

async function getData() {
  try {
    const data = await fetchData();
    console.log(data);
  } catch (error) {
    console.log(error);
  }
}

getData();

Benefit:

  • Cleaner error handling

  • Looks like synchronous try/catch


Promise vs Async/Await

Promise Style:

fetchData()
  .then((data) => {
    console.log(data);
  })
  .catch((err) => {
    console.log(err);
  });

Async/Await Style:

async function getData() {
  try {
    const data = await fetchData();
    console.log(data);
  } catch (err) {
    console.log(err);
  }
}

Key Differences

  • Promises use .then() and .catch()

  • Async/await uses await and try...catch

  • Async/await looks more readable and clean


Async Function Execution Flow (Concept)

Start → Call async function
       → await pauses inside function
       → Promise resolves
       → Execution resumes
       → End

Common Mistakes

  • Using await outside an async function

  • Forgetting try...catch

  • Not handling rejected promises


Interview Points

  • Async/await is syntactic sugar over promises

  • async functions return promises

  • await pauses execution inside async function

  • Better readability than promise chaining


Conclusion

Async/await makes asynchronous JavaScript:

  • Cleaner

  • More readable

  • Easier to manage

It is widely used in modern JavaScript, especially in backend development and API handling.