What are Callbacks in Javascript and how to use them

 · 10 mins read

Photo by John Jackson On Unsplash

Callbacks are a very important topic to understand in javascript. In this article, We will be seeing what Callbacks are and how to use them.

What are Callbacks

First, we will see a regular javascript function, and from there we will see how callbacks are used.

Regular Javascript function

So first let us look at a normal Function in javascript.

function multiply(a, b) {
  var result = a * b;
  console.log("multiply Function Result:",result);
}
multiply(2, 4);

Here we have a simple function which multiplies 2 numbers. We are then calling the function with inputs 2 and 4.

Callbacks Example 1

Now imagine if you had to run another operation immediately after multiply computes the result. This is where we use a callback. The Below code shows this.

function multiply(a, b, callback) {
  var result = a * b;
  console.log("multiply Function Result:",result);
  callback(result);
}
 
function checkOddEven(result){
    var isEven = result % 2 == 0
    if(isEven){
        console.log('checkOddEven Function:',result,' is Even');
    }
    else
    {
        console.log('checkOddEven Function:',result,' is Odd');
    }
}
 
multiply(7, 9, checkOddEven);

Here in the multiply function, we accept a callback as well as the input.

when we call the multiply function we pass callback as checkOddEven. So basically a callback is nothing but a function. checkOddEven is a function which checks whether a number is odd or even.

In the multiply function, at the end, we have callback(result). This is where we ask the callback function to execute.

So in the above code, the sequence is as follows

  • First we call the multiply function and pass checkOddEven as the callback
  • multiply function executes and computes the multiplication result
  • once the result is calculated, multiply function asks the callback to execute.
  • In this case, the callback is checkOddEven function. So checkOddEven function will execute.

The result of the above code is shown below

multiply Function Result: 63
checkOddEven Function: 63  is Odd

We can pass any function to the callback.

Callbacks Example 2

Let’s take the following script

function multiply(a, b, callback) {
  var result = a * b;
  console.log("multiply Function Result:",result);
  callback(result);
}
 
function checkPosNeg(result){
    var isPositive = result >= 0; 
    if(isPositive){
        console.log('checkPosNeg Function:',result,' is Positive');
    }
    else
    {
        console.log('checkPosNeg Function:',result,' is Negative');
    }
}
multiply(-7, 9, checkPosNeg);

Here we have a function called checkPosNeg which checks whether the number is positive or negative.

We are passing the callback as checkPosNeg in this Example.

The output of the above program is given below

multiply Function Result: -63
checkPosNeg Function: -63  is Negative

From this example, we see that any function can be passed to the callback.

Anonymous Callback Function

Another way of passing a callback is by using anonymous functions. The code for this is shown below.

function multiply(a, b, callback) {
  var result = a * b;
  console.log("multiply Function Result:", result);
  callback(result);
}
 
multiply(-7, 9, function(result) {
 
  if (result > 0) {
    console.log('checkPosNeg Function:', result, ' is Positive');
  } else {
    console.log('checkPosNeg Function:', result, ' is Negative');
  }
});

In this case, we see that the callback function is created at the same time we are calling the multiply function. This function basically checks whether the number is positive or negative but the function does not have any name.

Error Handling in Callbacks

The below code snippet shows how to do error handling in callbacks.

function divide(a, b, callback) {
  if (b != 0) {
    var result = a / b;
    console.log('divide Function Result', result);
    callback(null, result);
  } else
    callback(new Error('Divide by 0 Error:' + a + '/' + b))
}
 
function checkPosNeg(error, result) {
  if (error) {
    console.log('checkPosNeg Function cannot run');
    console.log(error);
  } else {
    var isPositive = result >= 0;
    if (isPositive) {
      console.log('checkPosNeg Function:', result, ' is Positive');
    } else {
      console.log('checkPosNeg Function:', result, ' is Negative');
    }
  }
}
 
divide(4, 0, checkPosNeg);

In this case, we have a function called divide which has a callback checkPosNeg.

Now when b is 0, then the division is not possible. If the division is not possible, then we cannot send any result to the callback.

So in this case we define the callback function as checkPosNeg(error,result).

Whenever division is possible we call callback(null,result) indicating there is no Error and everything is good.

If the division is not possible then we call callback(new Error(‘Error message’)) which tells that there is an error.

Now in checkPosNeg function we need to check for error as well. In case error is not null then we need to take necessary action in the code. For example, here we are just printing the error message.

Why do we need Callbacks

The obvious question which you might have is why do we even need callbacks.

Let’s take the following code snippet

console.log('Task1');
makeServerCall(url,function(error,result){
    console.log('Task2');
});
console.log('Task3');

In the Above code first Task1 is printed.

Next makeServerCall function makes a network call.

Now Will Task3 be printed before or after Task2?

Generally, whenever we make a network call, the Code continues to the next statement and does not wait for the result in sync.

So the moment the network call is made, the code continues to the next statement and prints Task3.

Once the network call completes and the response comes back then Task2 is printed.

So here makeServerCall takes a callback as its input. So once the server call completes, it executes the callback.

In this case, the callback enables us to run some operation once the network call is complete without blocking the code ( i.e the future statements are not blocked until the network call is complete).

Callback of Callbacks

Callbacks can be Chained Together.

Take the following code snippet.

function1(input1, function(error, result1) {
  function2(result1, function(error, result2) {
    function3(result2, function(error, result3) {
      console.log('Callback Chain')
    })
  })
})
  • Here first we wait for function1 to complete the network call and execute the first callback.
  • The first callback, in turn, calls function2. Once function2 completes its network call, it executes the second callback.
  • The Second callback calls function3. Once function3 completes its network call, it executes the third callback.
  • The Third callback just prints a message.

More callbacks can be chained together as well.

Something doesn’t seem right here

Well, as you might have already noticed in the above script, it becomes a little unreadable as the number of callbacks increase.

The above example shows just one-liner functions. If the functions were slightly bigger and the number of chained callbacks was more, then the code will be highly unreadable. Also, this means it is very very hard to debug the code.

A sample snippet is here to illustrate this

function1(input1, function(error, result1) {
  if (error) {
    console.log('Error')
  } else {
    function2(result1, function(error, result2) {
      if (error) {
        console.log('Error')
      } else {
        function3(result2, function(error, result3) {
          if (error) {
            console.log('Error')
          } else {
            function4(result3, function(error, result4) {
              if (error) {
                console.log('Error')
              }
            })
          }
        })
 
      }
    })
  }
})

This problem is known as the Pyramid of doom.

One way to get around this is to use Promises which I will be covering in a future article

Congrats 😄

You now know what are callbacks and how to use them.

Happy Coding 😄

Feel free to connect with me in LinkedIn or follow me in Twitter