Understanding Async/Await in JavaScript
When working with asynchronous code in JavaScript, we learned about Promises.
Promises solved the problem of callback hell, but they still introduced another issue:
👉 too many .then() chains
As the logic grows, even promises can become hard to read.
This is why async/await was introduced.
Why Async/Await Was Introduced
Let’s look at a promise-based example:
getData()
.then(data => processData(data))
.then(result => saveData(result))
.then(() => console.log("Done"))
.catch(error => console.log(error))
This is better than callbacks, but:
still chain-heavy
not very natural to read
looks different from normal code
Async/await was introduced to make asynchronous code look like normal synchronous code.
Promise vs Async/Await
What is an Async Function?
An async function is a function that always returns a promise.
async function greet() {
return "Hello"
}
greet().then(console.log)
Even though it returns a value, JavaScript wraps it inside a promise.
The Await Keyword Concept
The await keyword is used inside async functions.
It pauses the function until the promise is resolved.
Example:
async function getData() {
let response = await fetch("https://api.example.com/data")
let data = await response.json()
console.log(data)
}
Here:
awaitwaits for the resultcode runs step by step
This makes it feel like normal code.
Error Handling with Async Code
With promises, we used .catch().
With async/await, we use try...catch.
async function getData() {
try {
let response = await fetch("https://api.example.com/data")
let data = await response.json()
console.log(data)
} catch (error) {
console.log("Error:", error)
}
}
This looks like normal error handling and is easier to manage.
Comparison with Promises
Let’s compare both approaches.
Using Promises:
fetch("https://api.example.com/data")
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.log(error))
Using Async/Await:
async function fetchData() {
try {
let response = await fetch("https://api.example.com/data")
let data = await response.json()
console.log(data)
} catch (error) {
console.log(error)
}
}
Async/await is:
cleaner
easier to read
easier to debug
What Actually Happens Behind the Scenes
Important point:
async/await does NOT replace promises
it is built on top of promises
So internally, it still uses promises — just hides complexity.
How to Think About It
Instead of chaining .then():
Think like this:
wait for result
then continue
It makes async code feel like normal step-by-step execution.
One Line Summary
Async/await = a cleaner way to write promise-based code
Conclusion
Async/await improves readability by making asynchronous code look like synchronous code.
It simplifies promise handling, improves error management, and makes code easier to understand.
Once you start using async/await, it becomes the preferred way to handle asynchronous operations in JavaScript.