Callbacks & Callback Hell
Callbacks were the original way to handle async operations — pass a function to be called when the operation completes. But nested callbacks (callback hell) make code unreadable. This led to Promises.
Callbacks
- Callback — A function passed to an async function, called when the operation completes
- setTimeout(callback, ms) — The simplest async callback example
- Node-style callbacks — callback(error, result). Check error first: if (err) return handleError(err)
- Callback hell — Nested callbacks form a 'pyramid of doom'. Hard to read, debug, and maintain
- Inversion of control — You give control to another function. What if it calls your callback twice? Or never?
- Solution — Promises (and async/await) solve callback hell with cleaner, flat code
Callback Hell Code
// Simple callback
function fetchData(callback) {
setTimeout(() => {
const data = { user: "Alice", age: 25 };
callback(data);
}, 1000);
}
fetchData((data) => {
console.log("Got data:", data);
});
// ❌ Callback hell — pyramid of doom
function getUser(callback) {
setTimeout(() => callback({ id: 1, name: "Alice" }), 500);
}
function getOrders(userId, callback) {
setTimeout(() => callback([{ id: 101, item: "Laptop" }]), 500);
}
function getOrderDetails(orderId, callback) {
setTimeout(() => callback({ id: 101, status: "Shipped" }), 500);
}
// Nesting makes this unreadable...
getUser((user) => {
console.log("User:", user.name);
getOrders(user.id, (orders) => {
console.log("Orders:", orders);
getOrderDetails(orders[0].id, (details) => {
console.log("Details:", details);
// More nesting = more pain...
});
});
});
// This is WHY Promises were invented!
console.log("\n→ See next topic for Promises — the solution to callback hell");Tip
Tip
If you see callbacks nesting more than 2 levels deep, refactor to Promises or async/await immediately. Modern JavaScript has better tools — callbacks are only needed for event handlers and simple one-off operations.
async/await reads like sync code — the modern standard
Common Mistake
Warning
Deeply nested callbacks make error handling nearly impossible. Each level needs its own error check. Promises solve this with a single .catch() at the end of the chain.
Practice Task
Note
Experience callback hell: (1) Write 3 nested setTimeout callbacks. (2) Feel the pain of reading it. (3) Refactor the same logic into a Promise chain. (4) Then refactor to async/await. Compare readability.
Quick Quiz
Key Takeaways
- Callbacks were the original way to handle async operations — pass a function to be called when the operation completes.
- Callback — A function passed to an async function, called when the operation completes
- setTimeout(callback, ms) — The simplest async callback example
- Node-style callbacks — callback(error, result). Check error first: if (err) return handleError(err)