Async/Await in JavaScript: Writing Cleaner Asynchronous Code

In this article we'll understand the async-await keyword of JavaScript. Before understanding what something is, it is always a good idea to know why it exist at all. So to answer that question look at this code which isn't using async-await keyword:
function fetchUser() {
fetch("https://api.example.com/user")
.then(response => response.json())
.then(data => {
console.log("User:", data);
return fetch(`https://api.example.com/posts/${data.id}`);
})
.then(response => response.json())
.then(posts => {
console.log("Posts:", posts);
})
.catch(err => {
console.log("Error:", err);
});
}
At first glance, this might look fine. But as the logic grows:
Multiple
.then()chains make it harder to followError handling becomes less intuitive
Code readability decreases
This is exactly why async/await was introduced
Why Async/Await Was Introduced
Async/await is syntactic sugar over Promises.
It doesn’t replace Promises — it simply provides a cleaner and more readable way to write asynchronous code.
Problems it solves:
Deep
.then()chainingCallback-like nesting
Difficult debugging in complex flows
How Async Functions Work
An async function always returns a Promise.
async function greet() {
return "Hello";
}
This is equivalent to:
function greet() {
return Promise.resolve("Hello");
}
Even if you return a normal value, JavaScript wraps it inside a Promise.
Await Keyword Concept
The await keyword can only be used inside an async function.
It pauses execution until a Promise is resolved.
async function fetchUser() {
let response = await fetch("https://api.example.com/user");
let data = await response.json();
console.log("User:", data);
let postResponse = await fetch(`https://api.example.com/posts/${data.id}`);
let posts = await postResponse.json();
console.log("Posts:", posts);
}
What changed?
No
.then()chainingStep-by-step execution
Much easier to read and understand
It looks like synchronous code, but it’s still asynchronous under the hood.
Error Handling with Async Code
Instead of .catch(), async/await uses try...catch.
async function fetchUser() {
try {
let response = await fetch("https://api.example.com/user");
let data = await response.json();
let postResponse = await fetch(`https://api.example.com/posts/${data.id}`);
let posts = await postResponse.json();
console.log(posts);
} catch (error) {
console.log("Error:", error.message);
}
}
Why this is better:
Cleaner structure
Centralized error handling
Similar to synchronous programming
Comparison with Promises
Using Promises:
fetch("https://api.example.com/data")
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.log(err));
Using Async/Await:
async function getData() {
try {
let res = await fetch("https://api.example.com/data");
let data = await res.json();
console.log(data);
} catch (err) {
console.log(err);
}
}
Key Differences:
| Feature | Promises | Async/Await |
|---|---|---|
| Syntax | .then() chains |
Clean & linear |
| Readability | Medium | High |
| Error Handling | .catch() |
try...catch |
Why Async/Await Improves Readability
Code flows top-to-bottom
Less nesting
Easier to debug
More intuitive for beginners
You write async code the same way you think about it.
Final Thoughts
Async/await is one of the most important features in modern JavaScript.
It is built on top of Promises
Makes asynchronous code cleaner
Reduces complexity in real-world applications
Quick Summary
async→ makes a function return a Promiseawait→ pauses execution until Promise resolvesWorks only inside async functions
Uses
try...catchfor error handlingImproves readability significantly
Once you start using async/await, going back to .then() chains will feel unnecessarily complicated
.



