Callbacks & Callback Hell
📖 Concept
A callback is a function passed as an argument to another function, to be called when an operation completes. Before Promises, callbacks were the primary way to handle async operations.
Callback Hell (Pyramid of Doom) occurs when callbacks are nested inside callbacks, creating deeply indented, hard-to-read code.
Error-first callback pattern (Node.js convention): The first argument of the callback is an error (or null if successful).
🏠 Real-world analogy: Callbacks are like leaving a note for your roommate: "When the pizza arrives, put it in the oven." But callback hell is like: "When pizza arrives, put in oven. When oven beeps, cut pizza. When cut, serve on plates. When served, call everyone. When everyone seated..." — a chain of instructions that's hard to follow.
💻 Code Example
1// Basic callback pattern2function fetchUser(id, callback) {3 setTimeout(() => {4 callback(null, { id, name: "Alice" });5 }, 1000);6}78// Error-first callback (Node.js convention)9function readFile(path, callback) {10 setTimeout(() => {11 if (path === "/bad-path") {12 callback(new Error("File not found"));13 } else {14 callback(null, "File contents here");15 }16 }, 1000);17}1819readFile("/good-path", (err, data) => {20 if (err) {21 console.error(err.message);22 return;23 }24 console.log(data);25});2627// 😱 Callback Hell / Pyramid of Doom28function getUser(id, cb) {29 setTimeout(() => cb(null, { id, name: "Alice" }), 100);30}31function getPosts(userId, cb) {32 setTimeout(() => cb(null, [{ id: 1, title: "Post 1" }]), 100);33}34function getComments(postId, cb) {35 setTimeout(() => cb(null, [{ id: 1, text: "Nice!" }]), 100);36}3738// The pyramid of doom39getUser(1, (err, user) => {40 if (err) return console.error(err);41 getPosts(user.id, (err, posts) => {42 if (err) return console.error(err);43 getComments(posts[0].id, (err, comments) => {44 if (err) return console.error(err);45 console.log(user, posts, comments);46 // And deeper it goes...47 });48 });49});5051// ✅ Solution: Use Promises52function getUserPromise(id) {53 return new Promise((resolve) => {54 setTimeout(() => resolve({ id, name: "Alice" }), 100);55 });56}
🏋️ Practice Exercise
Mini Exercise:
- Convert a callback-based function to return a Promise
- Write callback-based code that reads 3 files in sequence, then refactor with Promises
- Implement a callback-based retry mechanism
- Create a
callbackifyfunction that converts a Promise-based function to use callbacks
⚠️ Common Mistakes
Forgetting to handle errors in callbacks — always check the error argument first
Calling the callback more than once — a callback should be called exactly once
Not returning after error callbacks — code continues to execute past the error handler
Mixing sync and async callbacks — if a function sometimes calls back synchronously and sometimes async, it creates confusing behavior
Using callbacks when Promises/async-await is available — callbacks are legacy for most async patterns
💼 Interview Questions
🎤 Mock Interview
Mock interview is powered by AI for Callbacks & Callback Hell. Login to unlock this feature.