Async/Await in JavaScript
Writing Cleaner Asynchronous Code

Introduction
JavaScript is single-threaded, but it handles asynchronous operations like APIs, file handling, and timers using powerful patterns. Before modern syntax, developers relied heavily on callbacks and promises — which often led to messy, hard-to-read code. That’s where async/await comes in.
Why Async/Await Was Introduced?
Before async/await, we used:
Callbacks → caused callback hell
Promises → improved structure but still had chaining complexity
Example with promises:
fetchData()
.then(data => processData(data))
.then(result => saveResult(result))
.catch(error => console.error(error));
This is better than callbacks, but still not as readable as synchronous code.
Async/await was introduced to:
Make async code look synchronous
Improve readability and maintainability
Simplify error handling
How Async Functions Work?
An async function always returns a Promise.
async function getData() {
return "Hello";
}
This is equivalent to:
function getData() {
return Promise.resolve("Hello");
}
Key Point:
asyncautomatically wraps return values in a Promise
Await Keyword Concept
The await keyword pauses execution until a Promise is resolved.
async function fetchUser() {
const response = await fetch("https://api.example.com/user");
const data = await response.json();
console.log(data);
}
What happens here:
Function execution pauses at
awaitPromise resolves
Execution resumes with the resolved value
Error Handling with Async Code
With promises:
fetchData()
.then(data => console.log(data))
.catch(err => console.error(err));
With async/await:
async function loadData() {
try {
const data = await fetchData();
console.log(data);
} catch (error) {
console.error(error);
} finally {
console.log("Done");
}
}
Benefits:
Cleaner structure
Works like synchronous
try...catchEasier debugging
| Feature | Promises (.then) | Async/Await |
|---|---|---|
| Syntax | Chain-based | Sequential |
| Readability | Medium | High |
| Error Handling | .catch() |
try...catch |
| Debugging | Harder | Easier |
Async/Await = Syntactic Sugar
Async/await does not replace promises. It is built on top of promises.
Example:
async function example() {
return 42;
}
Internally behaves like:
function example() {
return Promise.resolve(42);
}
So you’re still using promises — just with cleaner syntax.
Simple Example:
Using Promises:
function getNumber() {
return new Promise(resolve => {
setTimeout(() => resolve(10), 1000);
});
}
getNumber().then(num => console.log(num));
Using Async/Await:
async function showNumber() {
const num = await getNumber();
console.log(num);
}
showNumber();
Notice how it reads like normal synchronous code.
Simple Example
Using Promises:
function getNumber() {
return new Promise(resolve => {
setTimeout(() => resolve(10), 1000);
});
}
getNumber().then(num => console.log(num));
Using Async/Await:
async function showNumber() {
const num = await getNumber();
console.log(num);
}
showNumber();
Notice how it reads like normal synchronous code.
When to Use Async/Await
Use async/await when:
You want clean, readable code
You are dealing with multiple sequential async calls
You need structured error handling
Avoid overusing it when:
- You need parallel execution (use
Promise.all()instead)
Conclusion
Async/await is one of the most important modern JavaScript features. It makes asynchronous code:
Cleaner
Easier to understand
Easier to debug
But remember — it’s just a better way to work with promises, not a replacement.





