Skip to main content

Command Palette

Search for a command to run...

Async/Await in JavaScript: Writing Cleaner Asynchronous Code

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

Modern JavaScript applications perform many tasks that take time.

Examples:

  • Fetching data from APIs

  • Uploading files

  • Reading databases

  • Waiting for timers

  • User authentication

Handling these tasks using normal synchronous code can freeze applications.

To solve this problem, JavaScript introduced asynchronous programming.

Initially, callbacks were used.

Then promises were introduced.

Finally, async/await made asynchronous code cleaner and easier to understand.

In this blog, we will learn:

  • Why async/await was introduced

  • How async functions work

  • Await keyword concept

  • Error handling in async code

  • Comparison with promises

  • Internal working of async/await

Everything will be explained in simple language from basic to advanced.


What is Async/Await?

Async/await is a modern way to handle asynchronous operations in JavaScript.

It makes asynchronous code look like synchronous code.


Why Async/Await Was Introduced

Before async/await, developers mainly used:

  • Callbacks

  • Promises

Callbacks created deeply nested code.

This problem was called:

Callback Hell

Example:

loginUser(() => {
   getProfile(() => {
      getPosts(() => {
         getComments(() => {

         });
      });
   });
});

This becomes:

  • Hard to read

  • Difficult to debug

  • Difficult to maintain


Promises Improved the Situation

Promises made code better.

Example:

loginUser()
.then(() => getProfile())
.then(() => getPosts())
.then(() => getComments())
.catch(err => console.log(err));

This is cleaner than callbacks.

But long promise chains still become difficult sometimes.


Async/Await Solved This Problem

Async/await makes asynchronous code look simple.

Example:

async function getData() {

   const user = await loginUser();

   const profile = await getProfile();

   console.log(profile);
}

This looks almost like normal synchronous code.

That is why async/await became very popular.


Async/Await is Syntactic Sugar

Async/await does not replace promises internally.

It is built on top of promises.

This means:

Async/await is just a cleaner way to write promise-based code.


What is an Async Function?

An async function is a function declared using the async keyword.


Basic Syntax

async function greet() {

}

Important Rule

An async function always returns a promise.


Example

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

console.log(greet());

Output

Promise { 'Hello' }

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


Returning Values from Async Functions

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

getMessage().then(data => {
   console.log(data);
});

Output

Welcome

Understanding the Await Keyword

The await keyword pauses execution until a promise resolves.


Basic Example

function fetchData() {

   return new Promise((resolve) => {

      setTimeout(() => {
         resolve("Data Received");
      }, 2000);

   });
}

async function getData() {

   const result = await fetchData();

   console.log(result);
}

getData();

Output

Data Received

What Happened Internally?

Step-by-step:

  1. fetchData() returns a promise

  2. await pauses the function

  3. JavaScript waits for promise completion

  4. Promise resolves after 2 seconds

  5. Execution continues


Async Function Execution Flow

Start Async Function
        │
        ▼
Call Promise Function
        │
        ▼
await pauses execution
        │
        ▼
Promise resolves
        │
        ▼
Execution resumes
        │
        ▼
Result received

Why Await Makes Code Cleaner

Without async/await:

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

With async/await:

const data = await fetchData();

console.log(data);

Much easier to read.


Real Life Example

Imagine ordering food online.

Without async behavior:

You stand near the restaurant and wait doing nothing.

With async/await:

  • Order food

  • Continue your work

  • Wait only when food arrives

That waiting moment is similar to await.


Multiple Await Example

function stepOne() {
   return Promise.resolve("Step 1 Complete");
}

function stepTwo() {
   return Promise.resolve("Step 2 Complete");
}

async function processSteps() {

   const first = await stepOne();

   console.log(first);

   const second = await stepTwo();

   console.log(second);
}

processSteps();

Output

Step 1 Complete
Step 2 Complete

Sequential Execution

Await executes promises one by one.

Flow:

Step 1
   ↓
Wait
   ↓
Step 2
   ↓
Wait
   ↓
Finish

Async Functions Without Await

An async function can exist without await.

Example:

async function hello() {
   return "Hello World";
}

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

Output

Hello World

Error Handling in Async/Await

Errors are handled using try...catch.


Example

function fetchUser() {

   return new Promise((resolve, reject) => {

      let success = false;

      if(success) {
         resolve("User Found");
      } else {
         reject("User Not Found");
      }

   });
}

async function getUser() {

   try {

      const result = await fetchUser();

      console.log(result);

   } catch(error) {

      console.log(error);

   }
}

getUser();

Output

User Not Found

Why Try-Catch is Useful

Without try-catch:

  • Application may crash

  • Errors become difficult to manage

With try-catch:

  • Cleaner error handling

  • Better debugging

  • More stable applications


Promise vs Async/Await

Promises Async/Await
Uses .then() Uses await
More chaining Cleaner structure
Harder to read sometimes Looks synchronous
More nesting possible Better readability
Complex error handling Easy try-catch

Promise Flow Diagram

fetchData()
    │
    ▼
.then()
    │
    ▼
.then()
    │
    ▼
.catch()

Async/Await Flow Diagram

async function
       │
       ▼
await fetchData()
       │
       ▼
Result Stored
       │
       ▼
try-catch handles errors

Advanced Example with API

async function getUsers() {

   try {

      const response = await fetch(
         "https://jsonplaceholder.typicode.com/users"
      );

      const data = await response.json();

      console.log(data);

   } catch(error) {

      console.log("Error:", error);

   }
}

getUsers();

This is how APIs are commonly handled in modern applications.


Await Only Works Inside Async Functions

This is important.

Wrong:

const data = await fetchData();

This gives error.

Correct:

async function getData() {

   const data = await fetchData();

}

Parallel Execution with Promise.all

Sometimes multiple async tasks should run together.


Example

const promise1 = Promise.resolve("A");
const promise2 = Promise.resolve("B");

async function runTasks() {

   const result = await Promise.all([
      promise1,
      promise2
   ]);

   console.log(result);
}

runTasks();

Output

[ 'A', 'B' ]

Benefits of Async/Await

Benefit Explanation
Cleaner syntax Easy to understand
Better readability Looks synchronous
Easy debugging Structured code
Better error handling Uses try-catch
Less nesting Cleaner flow

Common Mistakes


1. Forgetting Await

async function test() {

   const data = fetchData();

   console.log(data);
}

This prints promise object instead of actual data.


Correct Version

const data = await fetchData();

2. Using Await Outside Async Function

Wrong:

await fetchData();

Correct:

async function run() {

   await fetchData();

}

Internal Working of Async/Await

Behind the scenes:

  • Async functions return promises

  • Await pauses execution temporarily

  • Event loop handles async operations

  • Execution resumes after promise completion


Async/Await Internal Architecture

Async Function
      │
      ▼
Await Promise
      │
      ▼
Promise sent to Web APIs
      │
      ▼
Event Loop waits
      │
      ▼
Promise resolved
      │
      ▼
Execution continues

Real World Usage of Async/Await

Async/await is heavily used in:

  • API calls

  • Database queries

  • Authentication systems

  • File uploads

  • Backend development

  • React applications

  • Node.js applications


Conclusion

Async/await is one of the best modern features in JavaScript.

It makes asynchronous programming:

  • Cleaner

  • Easier

  • More readable

  • Easier to debug

Although it works internally using promises, async/await provides a much simpler syntax for developers.

Understanding async/await is extremely important for modern web development because almost every real-world application uses asynchronous operations.

Thank you for reading this blog.

I hope this helped you understand async/await in JavaScript from basic to advanced concepts in a simple and practical way.

Your feedback is always appreciated.

1 views