Promises and Async/Await Made Simple
JavaScript • Concepts Explained • 7 min read
Asynchronous JavaScript confuses many beginners. This article breaks down Promises and async/await with real examples.
Promises and Async/Await Made Simple
If you have ever seen code with arrows pointing in every direction, you have encountered callback hell. Promises and async/await are the modern solution to handling asynchronous operations cleanly.
What Does Asynchronous Mean?
Asynchronous code lets your program start a task, move on to other work, and come back when the task is done. Fetching data from an API, reading a file, or waiting for a timer are all asynchronous operations.
console.log("First")
setTimeout(() => console.log("Second"), 1000)
console.log("Third")
// Output: First, Third, Second
Callbacks and Callback Hell
A callback is a function you pass to another function to run later. When you nest callbacks too deeply, the code becomes unreadable. This problem is called callback hell.
getUser(id, (user) => {
getOrders(user.id, (orders) => {
getOrderDetails(orders[0].id, (details) => {
console.log(details)
})
})
})
Promises: A Better Way
A Promise is an object that represents a value that will exist in the future. It can be in one of three states: pending, fulfilled, or rejected.
const myPromise = new Promise((resolve, reject) => {
const success = true
if (success) {
resolve("It worked!")
} else {
reject("Something went wrong")
}
})
myPromise
.then((message) => console.log(message))
.catch((error) => console.log(error))
Async/Await: Cleaner Syntax
Async/await is syntactic sugar over promises. An async function always returns a promise, and await pauses execution until the promise resolves.
async function fetchUser() {
try {
const response = await fetch("https://api.example.com/user")
const data = await response.json()
console.log(data)
} catch (error) {
console.log("Failed to fetch user:", error)
}
}
Real Example: Fetching Data
Here is the same API call written with promises and async/await side by side.
// With promises
fetch("https://api.example.com/posts")
.then((res) => res.json())
.then((data) => console.log(data))
.catch((err) => console.log(err))
// With async/await
async function getPosts() {
try {
const res = await fetch("https://api.example.com/posts")
const data = await res.json()
console.log(data)
} catch (err) {
console.log(err)
}
}
Error Handling
Promises use .catch() to handle errors. Async/await uses try/catch blocks. Both approaches let you gracefully handle failures without crashing your application.
Note: Use async/await for most new code. It is easier to read and debug. Callbacks are still fine for simple one-off operations like event listeners, and promises work well when you need to chain multiple async tasks.