Skip to main content

Command Palette

Search for a command to run...

Async/Await in JavaScript: Writing Cleaner Asynchronous Code

Simplifying Asynchronous Programming with Readable and Structured Syntax

Updated
5 min read
Async/Await in JavaScript: Writing Cleaner Asynchronous Code
R

Software engineer passionate about tech, innovation & research. I explore, build, and share insights on coding, systems, and emerging technologies.

Introduction

JavaScript frequently performs operations that take time, such as API requests, timers, and file processing. These operations are handled asynchronously so that the program does not stop executing while waiting for results.

Initially, callbacks were used to manage asynchronous behavior, which often led to deeply nested and hard-to-read code. Promises improved this by introducing chaining, but complex flows still became difficult to follow.

To address these challenges, JavaScript introduced async/await. This feature allows developers to write asynchronous code in a way that looks and behaves like synchronous code, improving readability and maintainability.

Why Async/Await Was Introduced

Before async/await, developers relied on Promises with .then() and .catch() chaining.

Example Using Promises

function getUserData() {
  return Promise.resolve("User Data");
}

getUserData()
  .then(data => processData(data))
  .then(result => console.log(result))
  .catch(error => console.log(error));

function processData(data) {
  return data + " Processed";
}

Problems with This Approach

  • Code becomes harder to read with multiple .then() calls

  • Logical flow is split across multiple functions

  • Error handling is separated from main logic

Async/await was introduced as syntactic sugar over Promises, making asynchronous code easier to read and write.

How Async Functions Work

An async function always returns a Promise, even if it appears to return a simple value.

Example

async function getMessage() {
  return "Welcome";
}

getMessage().then(console.log);

Explanation

  • The return value is automatically wrapped in a Promise

  • Equivalent to Promise.resolve("Welcome")

Key Concept

Await Keyword Concept

The await keyword pauses execution of an async function until a Promise is resolved.

Example

function fetchInfo() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve("Information loaded");
    }, 1500);
  });
}

async function displayInfo() {
  let result = await fetchInfo();
  console.log(result);
}

displayInfo();

Execution Flow

  • fetchInfo() starts execution

  • await pauses the function

  • Once Promise resolves, execution resumes

  • Result is stored in variable

Flow Representation

Error Handling with Async Code

Errors in async/await are handled using try...catch, similar to synchronous code.

Example

function fetchData() {
  return new Promise((resolve, reject) => {
    reject("Network error");
  });
}

async function loadData() {
  try {
    let data = await fetchData();
    console.log(data);
  } catch (error) {
    console.log("Error:", error);
  }
}

loadData();

Explanation

  • If Promise is rejected, control goes to catch

  • Keeps error handling close to logic

  • Improves readability compared to .catch()

Comparison with Promises

Feature Comparison

Feature Promises (.then) Async/Await
Syntax Chain-based Linear
Readability Moderate High
Error Handling .catch() try-catch
Complexity Harder for long chains Easier to manage

Example Comparison

Using Promises

fetchInfo()
  .then(data => console.log(data))
  .catch(err => console.log(err));

Using Async/Await

async function run() {
  try {
    let data = await fetchInfo();
    console.log(data);
  } catch (err) {
    console.log(err);
  }
}

run();

Conceptual Difference

Promises → Chain of operations
Async/Await → Step-by-step execution

Detailed Code Examples

Example 1: Basic Async Function

async function greetUser() {
  return "Hello User";
}

greetUser().then(console.log);

Example 2: Using Await

function wait() {
  return new Promise(resolve => {
    setTimeout(() => resolve("Finished"), 1000);
  });
}

async function execute() {
  let result = await wait();
  console.log(result);
}

execute();

Example 3: Multiple Await Calls

async function sequence() {
  let first = await wait();
  let second = await wait();

  console.log(first, second);
}

sequence();

Explanation

  • Each await waits for the previous one

  • Execution is sequential

Example 4: Error Handling

async function check() {
  try {
    let result = await Promise.reject("Operation failed");
    console.log(result);
  } catch (error) {
    console.log(error);
  }
}

check();

Common Mistakes

Using await Outside Async Function

// Invalid
let data = await fetchInfo();

Forgetting Async Returns Promise

async function test() {
  return 10;
}

// Actually returns Promise

Missing Error Handling

  • Not using try-catch can cause unhandled rejections

Sequential Execution of Independent Tasks

await task1();
await task2();

Better approach:

await Promise.all([task1(), task2()]);

Misunderstanding Execution Order

  • Async code does not block the entire program

  • Only pauses inside async function

Real-World Use Cases

API Request Example

async function getProfile() {
  try {
    let response = await fetch("https://api.sample.com/profile");
    let data = await response.json();

    console.log(data);
  } catch (error) {
    console.log("Failed to load profile");
  }
}

Sequential Loading Example

async function initializeApp() {
  await wait();
  console.log("Module 1 loaded");

  await wait();
  console.log("Module 2 loaded");
}

initializeApp();

Explanation

  • Steps execute in order

  • Each step waits for the previous one

Promise vs Async/Await Flow Diagram

Async Function Execution Flow Diagram

Conclusion

Async/await is a modern JavaScript feature that simplifies asynchronous programming by making code more readable and structured. It is built on top of Promises and provides a cleaner syntax for handling asynchronous operations.

This document covered:

  • Why async/await was introduced

  • How async functions behave

  • The working of the await keyword

  • Error handling with try-catch

  • Comparison with Promises

By mastering async/await, developers can write cleaner, more maintainable, and more predictable asynchronous code in real-world applications.