Skip to main content

Command Palette

Search for a command to run...

Understanding async and await in JavaScript

Sneak peek into async & await of Javascript

Published
•4 min read
Understanding async and await in JavaScript
G
I enjoy blending technology with business to build solutions that create real impact. I’m fascinated by how technology empowers people, businesses, and startups. I believe thoughtful use of technology quietly improves lives, and that belief fuels everything I build and explore 💻.

If you’ve worked with asynchronous JavaScript, you must have come across promises. They solve the problem of handling async operations, but after a point, chaining multiple promises starts getting messy and harder to read. That is exactly why async and await were introduced. The goal was not to replace promises, but to make working with them more readable and easier to manage.
So the important thing to keep in mind from the start is that async and await are just a cleaner way of writing promise based code. Internally, everything is still based on promises.

Why async and await were introduced

When promises were introduced, they improved things compared to callbacks, but chaining then and catch multiple times can still make code look complex. As the number of async operations increases, readability becomes an issue and debugging also gets harder.

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

fetchData()
  .then((data) => {
    console.log(data);
    return "Next step";
  })
  .then((res) => {
    console.log(res);
  })
  .catch((err) => {
    console.log(err);
  });

This works fine, but as more steps get added, it becomes harder to follow the flow. That is where async and await improve things by making the code look more like synchronous code.

How async functions work

An async function is just a function that always returns a promise. Even if you return a normal value, it will be wrapped inside a promise automatically.

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

greet().then((res) => console.log(res));

Here, even though we returned a string, it is treated as a resolved promise.
So the key idea is that async makes a function return a promise.

Await keyword concept

Await is used inside async functions to pause the execution until a promise is resolved. It makes the code look like it is running step by step, even though it is still asynchronous underneath.

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();

Here, await pauses the function at that line until fetchData resolves, and then continues execution. So the flow becomes much easier to read.

Comparison with promises

The same example using promises would look like this:

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

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

  fetchData().then((data) => {
    console.log(data);
    console.log("End");
  });
}

getData();

Both approaches achieve the same result, but the async and await version looks more linear and easier to follow.

Error handling with async code

Error handling becomes cleaner with async and await because we can use try and catch instead of chaining catch methods.

function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject("Something went wrong");
    }, 2000);
  });
}

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

getData();

This makes error handling look similar to synchronous code, which improves readability.

Final clarity

Async and await were introduced to make working with promises simpler and more readable. Async ensures that a function returns a promise, and await allows us to write asynchronous code in a step by step manner. Internally, everything is still based on promises, but the syntax makes it easier to understand and maintain.
So whenever you see async and await, think of them as a cleaner layer over promises that helps you write code that is easier to read and reason about.