Home » What is Callback In JS?

What is Callback In JS?

What is Callback In JS?

Introduction

Callback In JS, is one that passes as an argument to another function and executes after an event or task occurs. JavaScript treats functions as first-class citizens. This is useful in its own right because it allows the passing of functions as arguments to other functions or returning them from other functions. That is what makes callback functions so powerful.

How Do Callbacks Work?

Callbacks are commonly used in scenarios such as:

  • Event Handling: An event is, for instance, a click on a button or data loading. It will trigger a callback function to process the action.
  • Asynchronous Operations: For instance, data from a server needs to be fetched, or reading files asynchronously. When the operation is done, it will execute the callback.
  • Timers and Intervals: A function is executed after a delay or repeatedly after regular intervals when using the setTimeout() and setInterval() functions.

Types of Callbacks

There are two main types of callbacks in JavaScript:

Synchronous Callbacks: These are executed right away within the same stack frame as the higher-order function that uses them. Synchronous callbacks are quite common in functions like array forEach(), where the callback is invoked for each element synchronously.

function greet(name, callback) {
  console.log(`Hello, ${name}!`);
  callback();
}

function farewell() {
  console.log('Goodbye!');
}

greet('Alice', farewell);
JavaScript

Output:

Hello, Alice!
Goodbye!
JavaScript

Asynchronous Callback : These are callbacks which are executed at a later time, usually after the completion of an asynchronous operation. Examples that includes callbacks passed to functions like setTimeout() or AJAX requests.

function fetchData(callback) {
  setTimeout(() => {
    const data = 'Some data from server';
    callback(data);
  }, 2000);
}

function processData(data) {
  console.log(`Processing data: ${data}`);
}

fetchData(processData);
JavaScript

Output :

Processing data: Some data from server
JavaScript

Handling Callback Hell

One big problem in using callbacks excessively is what is often referred to as \”callback hell\” or \”pyramid of doom.\” This happens when multiple callbacks are nested, making the code very unreadable, unmanageable, and difficult to maintain. Asynchronous JavaScript frameworks like Promises and async/await were introduced to mitigate this issue.

Here is the Example:

// Callback hell example
asyncFunction1(function(result1) {
    asyncFunction2(result1, function(result2) {
        asyncFunction3(result2, function(result3) {
            // Nested callback functions continue...
            console.log('Result 3:', result3);
        });
    });
});

// Mock asynchronous functions
function asyncFunction1(callback) {
    setTimeout(function() {
        const result = 'Result 1';
        console.log('Result 1:', result);
        callback(result);
    }, 1000);
}

function asyncFunction2(data, callback) {
    setTimeout(function() {
        const result = 'Result 2 based on ' + data;
        console.log('Result 2:', result);
        callback(result);
    }, 1000);
}

function asyncFunction3(data, callback) {
    setTimeout(function() {
        const result = 'Result 3 based on ' + data;
        console.log('Result 3:', result);
        callback(result);
    }, 1000);
}
JavaScript

Output (Callback Hell):

Result 1: Result 1
Result 2: Result 2 based on Result 1
Result 3: Result 3 based on Result 2
JavaScript

In the above code of callback hell example, each asynchronous function is inside the callback of the previous function. Here is how the output corresponds to the code execution:

Result 1: This is the result of asyncFunction1 which executes after a delay of 1000ms.
Output 2: This is asyncFunction2 being run as a callback of asyncFunction1 with another delay of 1000 milliseconds based on the result received from asyncFunction1.
Output 3: The same applies to the function asyncFunction3. It has been called within the callback of the function asyncFunction2 and will be executed with another delay of 1000 milliseconds. It depends on the argument asyncFunction2 delivers.

Promises as an Alternative to Callbacks

Promises make working with asynchronous operations cleaner and more organized when compared with callbacks. A Promise represents the eventual completion (or failure) of an asynchronous operation, and it allows you to chain multiple asynchronous operations without having nested callbacks.

Here is the below Example of promise instead of callbacks

// Using Promises to mitigate callback hell
asyncFunction1()
    .then(asyncFunction2)
    .then(asyncFunction3)
    .then(result => {
        console.log('Final Result:', result);
    })
    .catch(error => {
        console.error('Error:', error);
    });

// Mock asynchronous functions
function asyncFunction1() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            const result = 'Result 1';
            console.log('Result 1:', result);
            resolve(result);
        }, 1000);
    });
}

function asyncFunction2(data) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            const result = 'Result 2 based on ' + data;
            console.log('Result 2:', result);
            resolve(result);
        }, 1000);
    });
}

function asyncFunction3(data) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            const result = 'Result 3 based on ' + data;
            console.log('Result 3:', result);
            resolve(result);
        }, 1000);
    });
}
JavaScript

In the above code :

  • The asyncFunction1, asyncFunction2, and asyncFunction3 functions simulate asynchronous operations using setTimeout.
  • Each function returns a Promise object, representing the eventual completion (or failure) of the asynchronous operation.
  • The .then() method is used to chain promises. Each .then() waits for the previous Promise to resolve before executing its own asynchronous operation
  • .The final .then() block receives the result of the last asynchronous operation and logs the final result.
  • The .catch() method is used to catch any errors that occur during the Promise chain.

Conclusion

Callbacks are one of the fundamental concepts in JavaScript for handling asynchronous operations. They are powerful, but too much usage can lead to callback hell and too much code to manage. Promises and async/await provide cleaner, more manageable alternatives to writing callback-based asynchronous code. Understanding the way callbacks work and the way around them is vital for becoming a professional in asynchronous programming with JavaScript.

Frequently Asked Questions

1. What is a callback function?

A callback function is a function passed as an argument to another function and is invoked after some kind of event or completion of a task. It is one of the common ways in which JavaScript uses for its handling of asynchronous operations.

2. How do callbacks work in JavaScript?

Callbacks work by passing a function as an argument to another function. A callback function allows for asynchronous behavior because it is run when the task associated with it is finished.

3. What is callback hell?

Callback hell, also known as the pyramid of doom, is the situation in which multiple nested callbacks cause code to become unreadable and unmaintainable. This usually happens when working with complex asynchronous operations.