Skip to main content

Command Palette

Search for a command to run...

Understanding Callback Functions in JavaScript

Updated
3 min readView as Markdown

Functions as First-Class Values

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

  • They can be stored in variables

  • Passed as arguments

  • Returned from other functions

function greet(name) {
  return "Hello " + name;
}

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

console.log(processUserInput(greet));

What’s happening:

  • greet is passed as a value

  • processUserInput executes it later

What is a Callback Function?

A callback function is:

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

Minimal Example

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

function execute(callback) {
  callback();
}

execute(sayHello);

Here:

  • sayHello = callback

  • execute = higher-order function

Why Callbacks Are Used

Problem Without Callbacks

JavaScript is single-threaded, but many operations take time:

  • API calls

  • File reading

  • Timers

If code runs sequentially:

console.log("Start");

setTimeout(() => {
  console.log("Data fetched");
}, 2000);

console.log("End");
Start
End
Data fetched

Root Issue

We cannot block execution while waiting.

Solution

Use callbacks to handle results after completion.

Callbacks in Asynchronous Programming

Example: Simulated API Call

function fetchData(callback) {
  setTimeout(() => {
    const data = { id: 1, name: "User" };
    callback(data);
  }, 2000);
}

function displayData(data) {
  console.log("Received:", data);
}

fetchData(displayData);

Flow:

  1. fetchData starts async task

  2. After delay → callback is executed

  3. Data is passed into callback

Passing Functions as Arguments

When you pass a function:

fetchData(displayData);

You are NOT calling it. You are passing a reference.

Wrong:

fetchData(displayData()); // executes immediately ❌

Correct:

fetchData(displayData); // passes function ✅

Common Callback Use Case

Event Handling

document.getElementById("btn").addEventListener("click", function () {
  console.log("Button clicked");
});

Array Methods

const numbers = [1, 2, 3];

numbers.forEach(function (num) {
  console.log(num);
});

Timers

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

The Problem: Callback Nesting (Callback Hell)

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

Issues

  • Deep nesting

  • Hard to read

  • Difficult error handling

  • Tight coupling of logic

Nested Callback Execution Flow

Observation:

  • Each step depends on previous result

  • Code forms a pyramid structure

Conceptual Understanding of the Problem

Root Cause

  • Sequential dependency of async tasks

  • No built-in structure for chaining

  • Control flow becomes fragmented

Result

  • Poor maintainability

  • Increased cognitive load

Summary

  • Functions in JavaScript can be passed and used as values

  • A callback is a function executed after another function completes

  • Callbacks solve async execution problems

  • They are widely used in events, APIs, and timers

  • Excessive nesting leads to callback hell

More from this blog