Skip to main content

📞 Callbacks

Leave your number, I'll call you back

The Call-Me-Back Analogy

You call a busy restaurant to make a reservation.

What happens:

  1. You call them
  2. They say: "We're busy right now. Leave your number, we'll call you back."
  3. You give your number and hang up
  4. You go about your day - shopping, working, whatever
  5. Later, the restaurant calls YOU with the answer

You didn't wait on the phone for a long time. You gave them a way to reach you ("call this number"), and they contacted you when they had an answer.

Callbacks work exactly the same way.

Instead of waiting for a slow operation to finish, you say: "Here's a function - call THIS when you're done." Then your code continues doing other things. When the operation completes, your callback function runs.


Why Callbacks Exist

The Problem: Some Operations Are Slow

  • Reading a file: often fast, but not instant
  • Fetching data from the internet: can vary from quick to noticeably slow
  • Querying a database: can vary a lot depending on load and the query

Without callbacks:

Your entire program would FREEZE while waiting. Click a button → website freezes for a few seconds → users leave angry.

With callbacks:

Your program keeps running. The slow operation happens in the background. When it's done, your callback executes with the result.

Think of it like a restaurant kitchen:

Without CallbacksWith Callbacks
Chef takes order, makes entire meal while customer stands at counter, then takes next orderChef takes order, gives ticket to kitchen, takes next orders. Kitchen calls out when meal is ready

The Basic Concept

A callback is just a function passed TO another function.

sendMessage(recipient, message, whatToDoWhenDone)
                                 ↑
                          This is the callback!

In plain English:

"Hey, send this message. When you're finished, run THIS function."

The "whatToDoWhenDone" function is the callback. It gets "called back" when the operation completes.


Where You Already Use Callbacks

You use callbacks more than you realize!

Click Handlers

When user clicks the button → run this function
                              ↑
                              callback!

Array Methods

For each item in the list → run this function
                            ↑
                            callback!

Timers

After a delay → run this function
                  ↑
                  callback!

Loading Data

When data arrives → run this function
                    ↑
                    callback!

How Callbacks Work (Step by Step)

Step 1: Define what should happen when done

function showResult(data) {
  console.log("Got the data:", data);
}

Step 2: Pass that function as an argument

fetchData(showResult); // "When you get data, call showResult"

Step 3: The receiving function runs your callback when ready

function fetchData(callback) {
  // ... do slow stuff ...
  // When done:
  callback(theData); // Runs showResult(theData)
}

Your function showResult gets "called back" with the result!


The Error-First Pattern

When things can go wrong, how do you report errors?

Common Node.js convention: error is usually the first parameter

callback(error, result)
         ↑       ↑
         If null, no error
         If has value, something went wrong

Why this pattern?

  • Consistency - teams usually know where to find errors
  • Can't forget to check - error is literally the first thing
  • Easy to short-circuit - if (error) return handleError(error);

Callback Hell: The Dark Side

The Problem:

When you need to do things in sequence (A, then B, then C, then D), callbacks nest deeper and deeper:

getUser ──→ then getOrders ──→ then getDetails ──→ then getShipping
   └──────────→ └──────────────→ └────────────────→ └───────────→
                                              Nested 4 levels deep!

This creates the infamous "Pyramid of Doom":

getUser(function(user) {
    getOrders(user, function(orders) {
        getDetails(orders, function(details) {
            getShipping(details, function(shipping) {
                // Finally we can do something!
                // But we're 4 levels deep...
            });
        });
    });
});

Why It's Problematic:

ProblemWhy It Hurts
Hard to readCode drifts right, hard to follow flow
Hard to debugWhich callback failed? Stack traces are confusing
Hard to handle errorsNeed error handling at EVERY level
Hard to reuseLogic is buried in nested functions

Escaping Callback Hell

Option 1: Named Functions (Flatten the code)

Instead of anonymous inline functions, use named functions:

getUser(handleUser);

function handleUser(user) {
  getOrders(user, handleOrders);
}

function handleOrders(orders) {
  getDetails(orders, handleDetails);
}

// ... etc

Still callbacks, but flattened and readable!

Option 2: Promises (Modern approach)

getUser()
  .then(user => getOrders(user))
  .then(orders => getDetails(orders))
  .then(details => getShipping(details))
  .catch(error => handleError(error));

Option 3: Async/Await (Common modern approach)

const user = await getUser();
const orders = await getOrders(user);
const details = await getDetails(orders);
const shipping = await getShipping(details);

Reads like normal code, no nesting!


Common Mistakes

Mistake 1: Forgetting Error Handling

// ❌ What if there's an error? We might miss it.
readFile(path, (err, data) => {
  console.log(data);  // data might be undefined!
});

// âś“ Typically check for errors first
readFile(path, (err, data) => {
  if (err) {
    console.error("Failed:", err);
    return;
  }
  console.log(data);
});

Mistake 2: Calling Callback Twice

// ❌ Bug: callback can be called twice!
function process(callback) {
  if (badThing) {
    callback("Error");
    // Forgot to return!
  }
  callback("Done");  // Also runs even after error!
}

// âś“ Return after calling callback
function process(callback) {
  if (badThing) {
    callback("Error");
    return;  // Stop here!
  }
  callback("Done");
}

Mistake 3: Expecting Immediate Results

// ❌ result is undefined - callback hasn't run yet!
let result;
fetchData((data) => { result = data; });
console.log(result);  // undefined!

// âś“ Use the result INSIDE the callback
fetchData((data) => {
  console.log(data);  // Data is available here
  doSomethingWith(data);
});

FAQ

Q: Are callbacks outdated?

No! Callbacks are still everywhere - click handlers, array methods (map, filter, forEach), event listeners. But for async operations, Promises and async/await are cleaner.

Q: What's a "higher-order function"?

A function that takes another function as an argument (like a callback) OR returns a function. Examples: map, filter, setTimeout.

Q: Why does JavaScript use callbacks so much?

JavaScript is single-threaded. While waiting for slow operations (network, disk), it can't freeze. Callbacks let other code run, and the callback executes when the slow thing finishes.

Q: Can I convert callbacks to Promises?

Yes! Wrap the callback-based function in a Promise. Most modern APIs already provide Promise versions.

Q: What's the difference between sync and async callbacks?

  • Sync callbacks run immediately (like forEach)
  • Async callbacks run later (like setTimeout or fetch)

Q: Should I still learn callbacks if async/await exists?

Yes! Callbacks are foundational. They help you understand what Promises and async/await do under the hood. Plus, you'll encounter callback-based code everywhere.


Summary

Callbacks are functions you pass to other functions, telling them "call this when you're done." They enable non-blocking code - your program can do other things while waiting.

Key Takeaways:

  • Callback = "call me back when you're done"
  • They prevent your code from freezing during slow operations
  • Error-first pattern: callback(error, result)
  • Callback hell = too many nested callbacks
  • Use named functions to flatten nesting
  • Modern alternatives: Promises and async/await
  • Handle errors consistently.
  • Don't forget return after calling callback

Even with async/await, understanding callbacks is essential - they're the foundation of asynchronous JavaScript!

Leave a Comment

Comments (0)

Be the first to comment on this concept.

Comments are approved automatically.