Skip to main content

Command Palette

Search for a command to run...

Callbacks in JavaScript: Why They Exist

Updated
5 min read
Callbacks in JavaScript: Why They Exist

Imagine you drop your clothes at a laundry shop. The worker doesn’t stand in front of the washing machine waiting for your clothes to finish cleaning. Instead, they take your clothes, start the machine, continue helping other customers, and later notify you when everything is ready.

That is exactly how callbacks work in JavaScript.

A callback is basically a function that says: “When this task finishes, run me.”

Callbacks are one of the oldest foundations of JavaScript. Before promises and async/await existed, callbacks handled timers, user interactions, API requests, and file operations. Even today, modern async features still rely on callback-like behavior underneath.

Once you understand callbacks properly, asynchronous JavaScript starts making a lot more sense.


Functions Are Treated Like Data

In many languages, functions feel separate from normal values. But in JavaScript, functions can behave just like strings, arrays, or objects.

You can store them in variables, pass them into functions, & return them from functions too.

Example:

function notify() {
  console.log("Notification sent");
}

const action = notify;

action();

Output:

Notification sent

Here’s the important distinction:

notify; //This means: “Here is the function itself.”

But:

notify(); //means: “Execute the function now.”

Callbacks only work because JavaScript allows functions to be passed around like values.


What Is a Callback Function?

A callback is simply a function given to another function so it can execute it later.

Example:

function generateReport(data, callback) {
  const report = `Total Sales: ${data}`;

  callback(report);
}

generateReport(5000, function(result) {
  console.log(result);
});

Output:

Total Sales: 5000

Flow:

generateReport()
       ↓
prepare report
       ↓
execute callback

Nothing magical is happening. The outer function performs some work. The callback decides what should happen afterward.


Why Pass Functions Into Other Functions?

Callbacks make code reusable and flexible. Imagine a shopping website where different buttons trigger different behaviors.

Example:

function handleAction(task) {
  task();
}

function addToCart() {
  console.log("Item added to cart");
}

function addToWishlist() {
  console.log("Item added to wishlist");
}

handleAction(addToCart);
handleAction(addToWishlist);

Output:

Item added to cart
Item added to wishlist

Instead of hardcoding behavior, the outer function lets callers decide what action should happen. That flexibility is one of the biggest reasons callbacks exist.


Why JavaScript Uses Callbacks for Async Work

JavaScript runs on a single thread. That means only one operation can execute at a time. If JavaScript paused every time it waited for something slow, websites would constantly freeze.

Imagine if Spotify stopped responding every time a song loaded. That would be terrible. So JavaScript handles long-running operations differently. Instead of waiting, it says:

“Start this task. When it’s done, run this callback.”

Example:

console.log("Uploading photo...");

setTimeout(function () {
  console.log("Photo uploaded successfully");
}, 3000);

console.log("User can still browse the app");

Output:

Uploading photo...
User can still browse the app
Photo uploaded successfully

JavaScript does not stop for 3 seconds.

Instead:

  1. it starts the timer

  2. stores the callback

  3. continues running other code

  4. executes the callback later

That is asynchronous programming.


Common Places Where Callbacks Appear

Callbacks are everywhere in JavaScript.

1. Timers

setTimeout(function () {
  console.log("Session expired");
}, 2000);

The callback executes later.


2. Event Listeners

loginButton.addEventListener("click", function () {
  console.log("User logged in");
});

The browser waits until the click happens, then runs the callback.


3. Array Methods

const temperatures = [30, 32, 35];

const fahrenheit = temperatures.map(function(temp) {
  return (temp * 9) / 5 + 32;
});

console.log(fahrenheit);

Output:

[86, 89.6, 95]

The callback decides how each item transforms.


4. Simulated API Requests

function fetchProfile(callback) {
  setTimeout(function () {
    callback("Profile Loaded");
  }, 2000);
}

fetchProfile(function(data) {
  console.log(data);
});

Output after 2 seconds:

Profile Loaded

This pattern was extremely common before promises became popular.


Why Callbacks Fit JavaScript So Well

JavaScript is built around events and reactions. Something happens → code responds.

Examples: user clicks a button, message arrives, timer completes, API responds, file finishes loading

Callbacks work perfectly because they describe actions that should happen later. They are basically delayed instructions.


The Major Problem: Nested Callbacks

Callbacks themselves are simple. But things become messy when many async tasks depend on each other.

Example:

authenticateUser(function(user) {

  loadDashboard(user, function(dashboard) {

    fetchNotifications(dashboard, function(notifications) {

      displayNotifications(notifications, function() {

        console.log("Everything loaded");

      });

    });

  });

});

Problems occurs when indentation grows deeper -> readability drops -> debugging becomes painful - > error handling repeats everywhere

Visualization:

Every operation waits for the previous callback. This deeply nested structure became known as:

Callback Hell

The issue was not callbacks themselves. The issue was managing complex asynchronous chains with endless nesting.

That frustration eventually led to: Promises & async/await

Those features improved readability while still building on the same core async ideas.


Common Beginner Mistake

A very common mistake is accidentally running the callback immediately.

Wrong:

setTimeout(showAlert(), 1000);

Correct:

setTimeout(showAlert, 1000);

Or:

setTimeout(function () {
  showAlert();
}, 1000);

Remember:

showAlert     → passing function
showAlert()   → executing function immediately

That small difference causes huge confusion for beginners.

Key Takeaways

  • A callback is a function passed into another function.

  • JavaScript uses callbacks heavily for async operations.

  • Functions behave like normal values in JavaScript.

  • Callbacks appear in timers, events, array methods, and APIs.

  • Async callbacks help JavaScript stay responsive.

  • Deep callback nesting caused “callback hell.”

  • Promises and async/await improved async readability later.

At its core, callbacks are simply JavaScript’s way of saying:

“When the work finishes, execute this function.”