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

codeTap to expand ⛶
1// Basic callback pattern
2function fetchUser(id, callback) {
3 setTimeout(() => {
4 callback(null, { id, name: "Alice" });
5 }, 1000);
6}
7
8// 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}
18
19readFile("/good-path", (err, data) => {
20 if (err) {
21 console.error(err.message);
22 return;
23 }
24 console.log(data);
25});
26
27// 😱 Callback Hell / Pyramid of Doom
28function 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}
37
38// The pyramid of doom
39getUser(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});
50
51// ✅ Solution: Use Promises
52function getUserPromise(id) {
53 return new Promise((resolve) => {
54 setTimeout(() => resolve({ id, name: "Alice" }), 100);
55 });
56}

🏋️ Practice Exercise

Mini Exercise:

  1. Convert a callback-based function to return a Promise
  2. Write callback-based code that reads 3 files in sequence, then refactor with Promises
  3. Implement a callback-based retry mechanism
  4. Create a callbackify function 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.