Skip to main content

Command Palette

Search for a command to run...

Callback Functions in JS: Why do they Exist?

Updated
4 min read

What is a Callback Function?

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

To break that down based on what we just went through:

  • "passed into another function as an argument": Instead of passing a string or a number to a function, we pass an actual, unexecuted block of code (the callback).

  • "invoked inside the outer function": The receiving function takes our code, does its own work first, and then literally calls your function.

  • "to complete a routine or action": It dictates what happens after the initial work is finished, whether that work was fetching data asynchronously or simply looping through an array synchronously.

Why do Callback Functions Exist?

The Problem: Synchronous Tasks Blocks the Thread

JavaScript is single-threaded language. It can only execute one line of code at a time. If a task takes five seconds to complete, a synchronous approach would freeze the entire application for those five seconds.

Ex: Imagine we have a function that fetches data from a database after which the code to load the UI is wriiten. If JavaScript executed this synchronously i.e. waiting for it to finish before moving on, and the database takes 3 seconds to respond, the browser locks up for 3 seconds and UI will load only after 3 seconds.

function fetchDatabaseSync(query) {
    // Executing Query -- takes 3 seconds
    user = { name: "Sam" };
    return user;
}

console.log("1. Application started");


const user = fetchDatabaseSync("SELECT * FROM users"); 

// The application freezes here until the data arrives and Clicks, scrolls, and animations are dead.

console.log("2. User data loaded:", user.name);
console.log("3. UI Updated");
1. Application started
2. User data loaded: Sam
3. UI Updated

The Solution: Asynchronous Callback Function

Because functions in JavaScript are first-class citizens, meaning they can be treated and passed like any other variable, we can pass a function into our fetch mechanism. Inside our fetch function, after successfully completing the fetch, the function passed will be called inside out function.

function fetchDatabaseSync(query, logUserData) {
    // Executing Query -- takes 3 seconds
    user = { name: "Sam" };    
    logUserData(user);
    return user;
}

console.log("1. Application started");


const user = fetchDatabaseSync("SELECT * FROM users", user => {
    console.log("2. User data loaded:", user.name);
}); 

// The application does not freeze here and Clicks, scrolls, and animations are applied.

console.log("3. UI Updated");
1. Application started
3. UI Updated
2. User data loaded: Sam

Advantage of Callback: Code Reusability

While callbacks are famous for handling asynchronous network requests, they are also deeply embedded in synchronous JavaScript. They exist here to make code highly modular.

Ex: Instead of writing for loops every time you want to manipulate an array, JavaScript provides methods that accept a callback function, executing it immediately for every item.

const numbers = [1, 2, 3, 4, 5];

// The function inside .filter() is a synchronous callback.
// It executes immediately for each element.
const evens = numbers.filter(num => {
    return num % 2 === 0;
});

console.log(evens); // Output: [2, 4]

Disadvantage of Callback: Callback Hell

Callbacks solved the single-thread problem, but the create a code structure problem when we have multiple asynchronous tasks that depend on the results of previous asynchronous tasks. The code looks like a pyramid also called Pyramid of Doom which are extremely difficult to debug.

// The Pyramid of Doom
getUser(userId, (user) => {
    getPosts(user.id, (posts) => {
        getComments(posts[0].id, (comments) => {
            getAuthorDetails(comments[0].authorId, (author) => {
                console.log("Finally got the author: ", author);
            });
        });
    });
});

JavaScript Fundamentals for Developers

Part 10 of 22

Learn JavaScript from the ground up with clear explanations and practical examples. This series covers core JavaScript concepts like variables, arrays, functions, loops, objects, and modern ES6 features to help you build a strong foundation in JavaScript.

Up next

Demystifying the 'this' Keyword in JavaScript

Preface: Before understanding 'this' let's understand a problem statement, where we have two objects of the same kind and each object has a variable and a method; and the method is trying to refer the