Callback in JavaScript in Simple English

Sam Ngu
4 min readSep 1, 2021

In short, Callback is a function that is passed to another function.

Also, view this tutorial on my YouTube Channel:

Callback hell is a horrible territory. Image by Author.

Callback functions exist everywhere in JavaScript. I’m pretty sure you have seen one of them around already.

// the 2nd argument of the addEventListener() function accepts a callback function, which is triggered when the specified event happens in the target DOM element// In this case, the callback function will run whenever the user click on anywhere in the DOMwindow.addEventListener('click', function(event){});

Why do we even need a callback function?

Callback function allows developers to define their own custom behaviour on top of some existing function.

You see, the author of the addEventListener() function doesn't know what do you want to do when you click on the DOM. So, the author somehow needs to provide a way for us to define our own custom logic, and the answer to this problem is Callback.

How does callback work behind the scene?

Let’s take a look at how callback works. Suppose we have a function called shopping() that takes in an budget argument, as the budget of a shopping trip 🤑.

function shopping(budget){
// suppose we spent $100 in our trip
const spent = 100;
// calculating the remaining budget
budget = budget - spent;
return budget;}

Calling the shopping() function should return the remaining budget after the trip. However, the users might want to do something after the shopping trip is finished, with the remaining budget. At the moment, they don't really have the flexibility to do so!

Shopping frenzy. Image from giphy

The solution is simple, we will introduce a Callback function as the second argument of the shopping() function.

function shopping(budget, afterShopping){
const spent = 100;
budget = budget - spent; // calling the afterShopping callback
afterShopping();
return budget;
}
// running the shopping function
shopping(1000, function(){
// this function is passed as the second argument,
// It becomes the 'afterShopping' callback
// and it will be called within the shopping function just before we return the budget
console.log("let's watch a movie");
});

Now if we run the code above, we would expect to see “let’s watch a movie” in the console.

Callbacks that have arguments

Let’s say the activity that we want to do after shopping is dependent on our remaining shopping budget. If we are running low on funds, there is really no point to do something extra, is there?

We define our activity in our callback function that we passed to the shopping function. It would be nice if we have access to the remaining budget right? So why don’t we pass the budget variable to the afterShopping callback?

function shopping(budget, afterShopping){
const spent = 100;
budget = budget - spent; // passing budget to the afterShopping callback
afterShopping(budget);
return budget;
}

Now, since we are passing budget to the afterShopping() callback, that means at the time when we write the function definition, we should make it accepts a budget argument.

// the afterShopping callback should now accept a 'budget' argument (or anything you'd like to call it)
shopping(1000, function(budget){
console.log(budget) // expect: 900
console.log("let's watch a movie");
});

If you compare our shopping() function call above against addEventListener(), can you see how similar they are? This is exactly what happened behind the scene with every Callback function in JavaScript.

window.addEventListener('click', function(event){
// ..
});

Returning data in Callbacks

Sometimes we see some Callback function require us to return some data. This is to get the callback function to return data back to its parent. Let’s see how this works.

For example, the activity that we do after shopping could cost us money, and we would like to deduct this cost from the remaining budget. Let’s redefine the shopping function.

function shopping(budget, afterShopping){
const spent = 100;
budget = budget - spent; // expecting the afterShopping callback function to return the remaining budget
budget = afterShopping(budget);
return budget;
}
const leftover = shopping(1000, function(budget){
console.log(budget) // expect: 900
console.log("let's watch a movie");
return budget - 50;
});
console.log(leftover); // expect 850

Now we are expecting the remaining budget from the afterShopping callback. That means, we need to get the callback function to return the remaining budget after the activity.

The leftover variable should reflect the remaining money after everything.

Callback Hell 😈

A place that one should never venture into.

Callback hell is a situation where we nest callbacks in callbacks. If you have no idea what I’m talking about, take a look at the following example.

Let’s say we want to go on another shopping trip after the first trip.

const leftover = shopping(1000, function(budget){
// we call the shopping function again in within the callback function
return shopping(budget, function(budget){
// ..
});
});

And another one:

const leftover = shopping(1000, function(budget){
return shopping(budget, function(budget){
return shopping(budget, function(budget){
// ..
});
});
});

Can you see where this is going?

const leftover = shopping(1000, function(budget){
return shopping(budget, function(budget){
return shopping(budget, function(budget){
return shopping(budget, function(budget){
return shopping(budget, function(budget){
return shopping(budget, function(budget){
// ..
});
});
});
});
});
});

We are going to nowhere but hell itself 😈.

The code above is very ugly and definitely not something that we want to read or debug. What can we do about this? There is actually a solution to this: to use Promises in JavaScript.

If you want to learn more about promises, check out my other article here:

That’s all about Callback!

--

--