Skip to main content

Command Palette

Search for a command to run...

Callbacks in JavaScript: Why They Exist

Updated
3 min read

Introduction :

JavaScript is famous for being asynchronous meaning it can handle multiple tasks without blocking the main thread. But how does it know what to do after a task finishes?

That’s where callbacks come in.

Before jumping into async magic, let’s start from the basics.

Functions as Values in JavaScript

In JavaScript, functions are first-class citizens. That means:

  • You can store them in variables

  • Pass them as arguments

  • Return them from other functions

Example:

function greet() {
  console.log("Hello!");
}

function executeFunction(fn) {
  fn(); 
}

executeFunction(greet);

Here, greet is passed as a value to another function.

What is a Callback Function?

A callback function is simply:

A function passed into another function as an argument, to be executed later.

Example:

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

function processUserInput(callback) {
  const name = "Mohit";
  callback(name);
}

processUserInput(greet);

greet is the callback here.

Why Callbacks Exist (The Real Reason)

JavaScript is single-threaded, meaning it can do only one thing at a time.

But in real-world apps, tasks like:

  • Fetching data from APIs

  • Reading files

  • Waiting for user actions

…take time

We don’t want the app to freeze while waiting.

So instead:

JavaScript delegates the task and says:

“Hey, when you're done, call this function.”

That function is the callback.

Callbacks in Asynchronous Programming

Example: setTimeout

console.log("Start");

setTimeout(() => {
  console.log("Executed after 2 seconds");
}, 2000);

console.log("End");

Output:

Start
End
Executed after 2 seconds

The function inside setTimeout is a callback, executed later.

Passing Functions as Arguments

This is the backbone of callbacks.

function calculate(a, b, operation) {
  return operation(a, b);
}

function add(x, y) {
  return x + y;
}

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

operation is a callback function.

Callback Usage in Real Scenarios

1. API Calls

fetch("https://api.example.com/data")
  .then((response) => response.json())
  .then((data) => {
    console.log(data);
  });

.then() uses callbacks internally.

2. Event Handling

button.addEventListener("click", () => {
  console.log("Button clicked!");
});

The function runs only when the event occurs**.**

3. File Handling (Node.js)

fs.readFile("file.txt", "utf8", (err, data) => {
  if (err) {
    console.error(err);
  } else {
    console.log(data);
  }
});

The function executes after file reading completes.

The Problem: Callback Nesting (Callback Hell)

When callbacks are nested too much, code becomes messy and hard to read.

Example:

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

This is called Callback Hell

Problems:

  • Hard to read

  • Difficult to debug

  • Error handling becomes messy

Conceptual Understanding

Think of callbacks like:

“Call me back when you're done.”

Instead of waiting, JavaScript continues doing other tasks.

Modern Solutions to Callback Problems

To solve callback hell, JavaScript introduced:

  • Promises

  • Async/Await

Example with async/await:

async function getData() {
  const response = await fetch("https://api.example.com/data");
  const data = await response.json();
  console.log(data);
}

Much cleaner and readable.

Conclusion

Callbacks are fundamental to JavaScript because they:

  • Enable asynchronous behavior

  • Prevent blocking execution

  • Allow flexible function composition

But while powerful, excessive use leads to complexity which is why modern patterns like Promises and async/await are preferred today.

Final Thought

Callbacks are not outdated they are the foundation on which modern async JavaScript is built.