JavaScript Callbacks Explained: Simple and Clear

Rishab RajRishab Raj
6 min read

What are functions?

Functions are reusable blocks of code that can be used whenever needed by simply calling their name and passing the required inputs as arguments.

function addNumbers(num1, num2) {
  // Calculate the sum of num1 and num2
  let sum = num1 + num2;  
  // Log the result to the console
  console.log("The sum is: " + sum);  
  // Return the sum
  return sum;
}
// function calling:
addNumbers(5, 7);  // Output: The sum is: 12

What are callbacks?

In JavaScript, callbacks are functions that take another function as an argument and execute when the function that received it completes its execution. In other words, when the receiving function finishes its task, it calls the callback function to continue the execution of the program.

Synchronous vs Asynchronous:

  • Synchronous: To understand this, let's take a real-life example. When you order something online, say from Amazon, you first add items to the cart, proceed to payment, and then wait until the order is delivered to your home. If Amazon worked synchronously, you would have to wait until the first shipment is delivered to your home before you could continue using Amazon or browsing. Wouldn't that be strange? This is where asynchronous programming, which JavaScript employs, comes into play.

      // Synchronous code example
      function orderItem() {
        console.log("Adding items to cart...");
        console.log("Proceeding to payment...");
    
        // Simulating blocking task (e.g., waiting for delivery)
        for (let i = 0; i < 1e9; i++) {} // Long-running task
    
        console.log("Order delivered!");
      }
    
      console.log("Start shopping on Amazon...");
      orderItem();  // Blocks everything until it's complete
      console.log("Continue browsing Amazon...");
    
      // Output:
      // Start shopping on Amazon...
      // Adding items to cart...
      // Proceeding to payment...
      // (Simulated delay here)
      // Order delivered!
      // Continue browsing Amazon...
    
  • Asynchronous: Now, taking the same example, you add items to your Amazon cart, pay, and place the order. In asynchronous programming, Amazon allows you to use the rest of the site without interruption by passing a callback function. Once the order is delivered to your home, the callback function is executed. In this way, the user experience remains smooth and uninterrupted.

      // Asynchronous code example
      function orderItemAsync(callback) {
        console.log("Adding items to cart...");
        console.log("Proceeding to payment...");
    
        // Simulating an asynchronous task (e.g., waiting for delivery)
        setTimeout(() => {
          console.log("Order delivered!");
          callback();  // Trigger the callback once order is delivered
        }, 3000); // 3 seconds delay to simulate waiting
      }
    
      console.log("Start shopping on Amazon...");
      orderItemAsync(() => {
        console.log("Notification: Your order has been delivered!");
      });
      console.log("Continue browsing Amazon...");
    
      // Output:
      // Start shopping on Amazon...
      // Adding items to cart...
      // Proceeding to payment...
      // Continue browsing Amazon...
      // (After 3 seconds)
      // Order delivered!
      // Notification: Your order has been delivered!
    

Why do we need Callback functions?

Javascript is single threaded language. That means it can perform only one task at a time so, when performing a slow operation such as fetching some file from the server or database or anything this situation could be problematic . It wouldn't be great user experience if the program froze until the data is returned.

One thing javascript deals with it is callback functions.

Javascript as an event driven language?

Javascript is an event driven language. That means it can listen and respond to the events while continuing to execute further code and without blocking its single thread.

And how does it do it ? Guess?

you guessed it right : Callbacks

Imagine if your program attached an event listener to a button and then sat there waiting for someone to click that button while refusing to do anything else. That wouldn’t be great!

Using callbacks we can specify what set of code should run in response to particular event.

First class and higher order functions:

Couple of buzzwords you can encounter in your journey of learning.

When we say that JavaScript supports first-class functions, this means that we can treat functions like a regular value. We can store them in a variable, we can return them from another function and, as we’ve seen already, we can pass them around as arguments.

And, higher-order functions, these are simply functions that either take a function as an argument, or return a function as a result. There are several native JavaScript functions that are also higher-order functions, such as setTimeout. Let’s use that to demonstrate how to create and run a callback.

How to create Callback functions?

It's simple create a callback function and pass it to the higher-order function as an argument:

// Function that takes two numbers and a callback function
function doMath(num1, num2, callback) {
  return callback(num1, num2);
}
// Callback function for addition
function add(a, b) {
  return a + b;
}
// Example usage with the add function
let result1 = doMath(5, 3, add);
console.log("Addition: " + result1); // Output: Addition: 8

Different kinds of Callback functions:

  1. Anonymous functions : A function definition without a name is known as an anonymous.

     // Anonymous function as a callback in setTimeout
     setTimeout(function() {
       console.log("Hello, world!"); 
     }, 1000);
    
  2. Arrow functions : Arrow functions were introduced with ES6. Due to their concise syntax, and because they have an implicit return value, they’re often used to perform simple one-liners, such as in the following example, which filters duplicate values from an array:

     const arr = [1, 2, 2, 3, 4, 5, 5];
     const unique = arr.filter((el, i) => arr.indexOf(el) === i);
     // [1, 2, 3, 4, 5]
    
  3. Named functions : There are two main ways to create named functions in JavaScript:

    1. Function declarations involve creating a function using the function keyword and giving it a name:

       function myCallback() {... }
       setTimeout(myCallback, 1000);
      
    2. Function expressions involve creating a function and assigning it to a variable:

       const myCallback = function() { ... };
       setTimeout(myCallback, 1000);
      

Things to keep in mind while using callback function:

Callback hell : when multiple asynchronous functions rely on one another and are nested inside each other. This can lead to code that's difficult to read and maintain. Below is an example of callback nesting:

This example simulates ordering an item, processing the payment, shipping the product, and confirming delivery, with each step dependent on the previous one. The callbacks are nested inside one another, leading to a "callback pyramid."

// Simulating nested asynchronous calls (callback hell)
function addToCart(product, callback) {
  setTimeout(() => {
    console.log(`Added ${product} to the cart.`);
    callback();
  }, 1000);
}

function processPayment(callback) {
  setTimeout(() => {
    console.log("Payment processed.");
    callback();
  }, 2000);
}

function shipOrder(callback) {
  setTimeout(() => {
    console.log("Order shipped.");
    callback();
  }, 3000);
}

function deliverOrder(callback) {
  setTimeout(() => {
    console.log("Order delivered.");
    callback();
  }, 1000);
}

// Starting the process
console.log("Start shopping on Amazon...");

addToCart("Laptop", () => {
  processPayment(() => {
    shipOrder(() => {
      deliverOrder(() => {
        console.log("Notification: Your order has been delivered!");
      });
    });
  });
});

console.log("Continue browsing Amazon...");

// Output:
// Start shopping on Amazon...
// Continue browsing Amazon...
// (After 1 second) Added Laptop to the cart.
// (After 2 more seconds) Payment processed.
// (After 3 more seconds) Order shipped.
// (After 1 more second) Order delivered.
// Notification: Your order has been delivered!

Soloution ? To tackle this problem ?

Prefer more modern methods of flow control

For example, promises and async...await

Want to learn more ?

Check out my another blog to learn and have insights of it to expand your horizons in the world of javascript.

0
Subscribe to my newsletter

Read articles from Rishab Raj directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Rishab Raj
Rishab Raj

I am currently a student working and hoping to make a career in tech which excites me as anything! here, to share and gain knowledge .