Skip to main content

Command Palette

Search for a command to run...

Callbacks in JavaScript: Why They Exist

Updated
4 min readView as Markdown
Callbacks in JavaScript: Why They Exist

JavaScript is a language full of surprises. One of the first things that trips people up is the idea of a callback. It sounds fancy, but once you get it, you'll wonder why you ever found it confusing.

Let's break it down simply.


1. What a callback function is

In JavaScript, functions are not special locked boxes. They're values — just like a number or a string. You can store a function in a variable, return it from another function, and yes, pass it as an argument to another function.

A callback is just a function you hand over to another function, saying: "Hey, run this when you're done."

function greet(name) {
  console.log("Hello, " + name);
}

function processUser(callback) {
  callback("Aryan");
}

processUser(greet); // Hello, Aryan

That's it. greet is a callback here. No magic.


2. Why callbacks are used in asynchronous programming

JavaScript runs one thing at a time. It doesn't wait.

Imagine you order food at a restaurant. You don't stand frozen at the counter staring at the wall until your food arrives. You sit down, talk to someone, check your phone. When the food is ready, the waiter comes to you.

That's async JavaScript. You kick off a task — fetching data, reading a file, waiting for a timer — and instead of freezing, JavaScript moves on. When the task finishes, it calls your callback.

console.log("Start");

setTimeout(function () {
  console.log("Done waiting");
}, 2000);

console.log("End");

// Output:
// Start
// End
// Done waiting (after 2 seconds)

Without callbacks, you'd have no way to say "do this when that finishes."


3. Passing functions as arguments

This is the foundation that makes callbacks work. Since functions are values in JavaScript, you can pass them around freely.

function add(a, b) {
  return a + b;
}

function calculate(num1, num2, operation) {
  return operation(num1, num2);
}

console.log(calculate(5, 3, add)); // 8

Here, add is passed into calculate as a value. Inside calculate, it's called as operation(...). The function doesn't know or care what operation is — it just calls it.

This pattern is powerful because it makes code flexible and reusable.


4. Callback usage in common scenarios

Callbacks show up everywhere in JavaScript:

Event listeners

document.querySelector("button").addEventListener("click", function () {
  console.log("Button clicked!");
});

The function runs only when the button is clicked.

Array methods

const numbers = [1, 2, 3, 4];
const doubled = numbers.map(function (n) {
  return n * 2;
});
// [2, 4, 6, 8]

map calls your function once for each item.

Timers

setTimeout(function () {
  console.log("3 seconds passed");
}, 3000);

In all these cases, you're handing a function to something else and saying: "Call this at the right moment."


5. Basic problem of callback nesting

Callbacks work well — until they don't.

When one async task depends on another, which depends on another, you start nesting callbacks inside callbacks. It gets messy fast.

getUser(function (user) {
  getOrders(user.id, function (orders) {
    getOrderDetails(orders[0].id, function (details) {
      console.log(details);
    });
  });
});

This is nicknamed callback hell — and for good reason. The code keeps sliding to the right, it's hard to read, and errors become a nightmare to handle.

This problem is real, and it's exactly why JavaScript later introduced Promises and async/await — cleaner ways to handle the same async situations without the nesting chaos.

But that's a story for another post.


Wrapping Up

Callbacks are one of JavaScript's oldest and most important ideas. They exist because JavaScript needed a way to say "do this later" without stopping everything else.

Once you understand that a function is just a value you can pass around, callbacks stop being scary. They're just functions handed off to run at the right time.

Simple as that.