Skip to main content

Command Palette

Search for a command to run...

Callbacks in JavaScript

Why They Exist?

Updated
3 min read
Callbacks in JavaScript
S
Software Developer | Full Stack Developer |

What is a Callback Function?

A callback function is a function passed into another function as an argument, which is then invoked inside the outer function to complete some kind of routine or action.

Example:

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

function executeCallbackFunction(callback) {
  callback();
}

executeCallbackFunction(sayHello);

There are two ways in which the callback may be called: synchronous and asynchronous. Synchronous callbacks are called immediately after the invocation of the outer function, with no intervening asynchronous tasks, while asynchronous callbacks are called at some point later, after an asynchronous operation has completed.

// Synchronous
function doSomething(callback) {
  callback();
}

// Asynchronous
function doSomething(callback) {
  setTimeout(callback, 0);
}

Why Callbacks are used in Asynchronous Programming?

JavaScript is single-threaded, meaning it executes one task at a time. But real-world tasks take time. These tasks are, for example:

  • Fetching data from an API

  • Reading files

  • Waiting for user input

Callbacks help us handle these delays without blocking execution.

Example with setTimeout:

console.log('Start');
setTimeout(() => {
  console.log('This runs later');
}, 2000);
console.log('End');

// Output:
// Start
// End
// This runs later

The callback inside setTimeout runs after 2 seconds, without stopping the rest of the code.

Passing functions as Arguments

This is where callbacks become powerful.

Example:

function calculate(a, b, operation) {
  return operation(a, b);
}
function add(x, y) {
  return x + y;
}
function multiply(x, y) {
  return x * y;
}
console.log(calculate(2, 3, add)); // 5
console.log(calculate(2, 3, multiply)); // 6

The behavior of calculate changes based on the callback passed.

Callback usage in common scenarios

  1. Event Handling:
button.addEventListener('click', () => {
  console.log('Button clicked!');
});
  1. Array Methods:
const numbers = [1, 2, 3];
const doubled = numbers.map((num) => num * 2);
console.log(doubled); // [2, 4, 6]
  1. API Calls (Before Promises):
function fetchData(callback) {
  setTimeout(() => {
    callback('Data received');
  }, 1000);
}
fetchData((data) => {
  console.log(data);
});

The Problem of Callback Nesting

When callbacks depend on other callbacks, code can become messy and hard to read. Example:

setTimeout(() => {
  console.log('Step 1');
  setTimeout(() => {
    console.log('Step 2');
    setTimeout(() => {
      console.log('Step 3');
    }, 1000);
  }, 1000);
}, 1000);

This pyramid structure is known as callback hell.

Problems:

  • Hard to read

  • Hard to debug

  • Hard to maintain

Think of it like this:

  • You’re waiting for Task A

  • Then inside it, waiting for Task B

  • Then inside it, waiting for Task C

Each step is dependent on the previous one, creating deep nesting.

Conclusion

Callbacks are the backbone of asynchronous JavaScript. Even modern tools like Promises and async/await are built on top of this concept.

If you understand callbacks well, everything else becomes much easier.

Debunking Fundamentals of JavaScript

Part 19 of 24

This series contains various blogs which explain the basics of javascript from scratch and give an inside working structure of the scripting language.

Up next

Error Handling in JavaScript

Try, Catch and Finally