DEV Community

Sakshi Tambole
Sakshi Tambole

Posted on

Callbacks in JavaScript: Why They Exist

JavaScript is a language where functions are treated like values. This means you can store them in variables, pass them to other functions, and return them from functions. This ability is what makes callbacks possible.

Callbacks are one of the most important concepts in JavaScript, especially when dealing with asynchronous programming like API calls, timers, and event handling.

Functions as Values in JavaScript

In JavaScript, functions are first-class citizens, which means they can be used just like any other variable.

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

const sayHello = greet;
sayHello();

Enter fullscreen mode Exit fullscreen mode

Here, the function greet is assigned to sayHello, and then called using the new variable.

functions as arguments to other functions.

function greetUser(name, callback) {
    console.log("Hi " + name);
    callback();
}

function sayBye() {
    console.log("Goodbye!");
}

greetUser("Sakshi", sayBye);


Output:
Hi Sakshi
Goodbye!

Enter fullscreen mode Exit fullscreen mode

In this example, sayBye is passed into greetUser as an argument. This passed function is called a callback function.

What is a Callback Function is

A callback function is a function that is passed as an argument to another function and is executed later.

It “calls back” after the main function finishes its work.

Example:

function processData(callback) {
    console.log("Processing data...");
    callback();
}

processData(function() {
    console.log("Data processed successfully!");
});

Output:
Processing data...
Data processed successfully!

Enter fullscreen mode Exit fullscreen mode
  • processData() starts running
  • It prints "Processing data..."
  • Then it executes the callback function

Why Callbacks Exist

Callbacks exist because sometimes JavaScript needs to wait for a task to complete before running the next step.

Examples:

  • Fetching data from a server
  • Waiting for a user click
  • Reading a file
  • Running a timer

Why callbacks are used in asynchronous programming

JavaScript runs in a single thread, meaning it executes one task at a time.

console.log("Start");

setTimeout(function() {
    console.log("Done after 2 seconds");
}, 2000);

console.log("End");

Output:
Start
End
Done after 2 seconds

Enter fullscreen mode Exit fullscreen mode

Even though setTimeout() appears in the middle, JavaScript does not wait 2 seconds.

Instead:

  • "Start" is printed
  • Timer begins in the background
  • "End" is printed immediately
  • After 2 seconds, callback runs

This is asynchronous behavior.

Without callbacks, JavaScript would have to pause execution until the task complete.

Callbacks allow JavaScript to remain fast and non-blocking.

Passing functions as arguments

Callbacks rely on the ability to pass functions as arguments.

function calculator(a, b, operation) {
    operation(a, b);
}

function add(x, y) {
    console.log(x + y);
}

calculator(5, 3, add);


Output:
8

Enter fullscreen mode Exit fullscreen mode
  • calculator() receives numbers and a function
  • add() is passed as the callback
  • calculator() runs the callback with the numbers

This pattern is widely used in JavaScript.

Callback usage in common scenarios

Callbacks are commonly used in real-world JavaScript.

  1. Event Handling

When a button is clicked, a callback runs.

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

Enter fullscreen mode Exit fullscreen mode

The click event may happen later, so JavaScript stores the callback until needed.

  1. Timers
setTimeout(function() {
    console.log("Executed later");
}, 1000);

Enter fullscreen mode Exit fullscreen mode

The callback executes after 1 second.

  1. Array Methods

Functions like forEach() use callbacks.

const numbers = [1, 2, 3];

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

Output:
1
2
3

Enter fullscreen mode Exit fullscreen mode

The callback runs once for each array element.

The Basic Problem of Callback Nesting

Callbacks are useful, but when multiple asynchronous tasks depend on each other, callbacks can become deeply nested.

loginUser(function(user) {
    getProfile(user, function(profile) {
        getPosts(profile, function(posts) {
            console.log(posts);
        });
    });
});

Enter fullscreen mode Exit fullscreen mode

This structure keeps growing inward.

This is called callback nesting or callback hell.

Problems include:

  • Hard to read
  • Difficult to debug
  • Difficult to maintain
  • Error handling becomes messy

This pyramid shape is often called the “Pyramid of Doom.”

Explain callback problems conceptually

Imagine:

  • Login user
  • Get profile
  • Get posts
  • Display posts Each step waits for the previous one. That creates nested callbacks:
step1(function() {
    step2(function() {
        step3(function() {
            step4(function() {
                console.log("Done");
            });
        });
    });
});

Enter fullscreen mode Exit fullscreen mode

The more steps you add, the harder the code becomes to manage.

This is why JavaScript introduced Promises and async/await to improve asynchronous code readability.

  1. Function Calling Another Function Flow
Main Function
     |
     v
 Executes Callback
     |
     v
 Callback Function Runs

Enter fullscreen mode Exit fullscreen mode
  1. Nested Callback Execution Flow
Task 1
  |
  --> Task 2
         |
         --> Task 3
                |
                --> Task 4

Or visually:

step1(
   step2(
      step3(
         step4()
      )
   )
)

Enter fullscreen mode Exit fullscreen mode

Top comments (0)