Skip to main content

Command Palette

Search for a command to run...

Async/Await in JavaScript: Writing Cleaner Asynchronous Code

Making Asynchronous Code Feel Synchronous (Without Breaking JavaScript)

Updated
•3 min read•View as Markdown

Introduction

If you've worked with asynchronous JavaScript, you've probably seen .then() chains from Promises.
They work but after a point, they start to feel messy and hard to read.
That's exactly why async/await was introduced.

It helps you write asynchronous code in a way that looks and feels more like normal, synchronous code.


Why Async/Await Was Introduced

Before async/await we had,

  • Callbacks --> messy nesting (callback hell)

  • Promises --> better, but still chained and less readable i complex flows.

Example with Promises:

fetchData()
  .then(res => res.json())
  .then(data => console.log(data))
  .catch(err => console.log(err));

This is fine for small cases. But as logic grows, chaining becomes harder to manage.

Async/await solves this by flattening the structure.


How Async Functions Work

An async function always return a Promise.

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

Even though it returns a sting, JavaScript wraps it in a Promise:

greet().then(console.log); // Hello

So internally,

  • async = function that returns a Promise

  • You don't need to manually create one


The Await Keyword (Core Concept)

await pauses the execution of an async function until a Promise resolves.

async function getData() {
  const response = await fetch("https://api.example.com/data");
  const data = await response.json();

  console.log(data);
}

What is happening here,

  • JavaScript waits for fetch() to complete

  • Then waits for.json() to complete

  • Them moves forward

It looks synchronous, but it’s still asynchronous under the hood.


Comparing with Promises

function getData() {
  fetch("https://api.example.com/data")
    .then(res => res.json())
    .then(data => console.log(data))
    .catch(err => console.log(err));
}

Now with async/await:

async function getData() {
  try {
    const res = await fetch("https://api.example.com/data");
    const data = await res.json();

    console.log(data);
  } catch (err) {
    console.log(err);
  }
}

Difference:

  • Promises → chaining

  • Async/await → step-by-step flow

Much easier to read and debug.


Error Handling with Async Code

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

async function getUser() {
  try {
    const res = await fetch("wrong-url");
    const data = await res.json();

    console.log(data);
  } catch (error) {
    console.log("Something went wrong:", error);
  }
}

This feels like normal error handling in synchronous code.


A Simple Real-Life Example

Let’s simulate an API call:

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

Using Promises:

fetchUser().then(data => console.log(data));

Using async/await:

async function showUser() {
  const data = await fetchUser();
  console.log(data);
}

showUser();

Cleaner. Easier to follow. Less mental load.


Why Async/Await Feels Better

  • Code looks synchronous

  • Easier to read

  • Easier to debug

  • Works well with try...catch

  • Reduces nested logic

Important:
Async/await is just syntactic sugar over Promises — it doesn’t replace them.


Small Edge Case to Know

If you use multiple awaits one after another, they run sequentially, not in parallel.

const a = await task1();
const b = await task2();

This can slow things down.

Better approach when tasks are independent:

const [a, b] = await Promise.all([task1(), task2()]);

Conclusion

Async/await doesn’t change how JavaScript works internally — it just makes your code cleaner and easier to understand.

If Promises were about control,
async/await is about clarity.

1 views