It is very important to understand Javascript callbacks and how they work in order to do awesome things in JavaScript. Actually a callback is nothing but a function passed to other function which gets called at some later instant (or immediately). Consider the following code

window.addEventListener("resize", handleResize);
function handleResize(event) {
    //do stuff
}

Here we’ve passed handleResize function to addEventListener(). So handleResize can be called a callback. What happens is, each time we resize the window, handleResize gets called with an event object (object containing information of that event) passed in as the argument

handleResize({
    type: "resize",
    target: Window
  
    //... other properties
});

To define a callback more precisely, we can say a callback is a function passed in to other function and might be needed to be called at some instant in future. In the context of the above code we can casually say- we leave it to the callback handleResize to manage everything when the window is resized.

Why is it important to understand Javascript Callbacks?

Callbacks are not just for jumping around functions but they also provide a backbone when you play with Javascript.

  • A bunch of APIs (even ES6s Promise API) make use of them. Those APIs would seem a mystery to you if you don’t know how callbacks work.
  • Understanding them well will power you to use them in your own APIs.

How Callbacks are implemented

You might be excited to try your hands on this stuff, but before playing around with it, you’ll need to learn how to implement them.

var nums= [1,2,3,4];
nums.map(function makeSquare(num) {
    return num*num;
}); // [1,4,9,16]

Here makeSquare is called a callback that we pass to the map() method. What map() does is that it utilises makeSquare to map a new array. But how? To answer to this ‘how’ is given by how it is implemented. Let’s see how map is basically implemented

Array.prototype.map= function(modifier) {
    var newArray= [];
    for(var i=0; i < this.length; i++) {
        var newValue= modifier(this[i]);
        newArray.push(newValue);
    }
    return newArray;
}

Let’s break it down! When we call map on nums, Array.prototype.map gets called (as all arrays delegate to Array.prototype object, see this post) with makeSquare as it’s argument and this binding set to nums. Inside this method we create an empty array newArray. Next we iterate over nums values and in each iteration we use modifier (which is makeSquare currrently) to get a new value based on itself and push this value to newArray. After iteration finishes we return newArray packed with the new values based on makeSquare method.

The same way is implemented Array.prototype.forEach

Array.prototype.forEach= function(performer) {
    for(var i=0; i < this.length; i++) {
        performer(this[i], i, this);
    }
}

It calls performer for each value of the array forEach is called on. Now consider the following code snippet

var buttons= document.querySelectorAll("button"), // [button1, button2, button3]
    actions= { 
      action1: function() {
    }
    //... other functions
}

buttons.forEach(function initialiseActions(button, index, buttonsArray) {
    var action= this.getAttribute("data-action");
    button.addEventListener("click", actions[action]);
});

For the above forEach call, initialiseActions() gets called three times in the following way.

initialiseActions(button1, 0, buttons);
initialisrActions(button2, 1, buttons);
initialiseActions(button3, 2, buttons);

To explain the code above casually, the callback initialActions is the servant of the master forEach. The master orders his servant with some instructions to do the work he (the servant) is capable of as many times as he (the master) requires that work to be done. So in the above code, forEach, the master did the work of assigning an event listener to all the three buttons with the help of his servant initialiseActions.

P.S. Never ever replace the native methods like we just did above. If you’d like to use your version of already existing methods, differ their names e.g.- _map for map.

Writing Callback-style Code

Callback-style code, whether async or not, provides a good interface to handle a task. You just have to leave your tasks to your callbacks. Also, it’s a very smart idea to use callbacks excessively in your APIs. With that said, let’s create something making use of a callback.

o we’re going to code a function which performs a given task at a given time (we’re not going worry about timezones here, as it’s just an example)

function executeAt(time, func) {
    var _date= (time.split(",")).map(function(t) {
        return parseInt(t);
    }),
    currentTime= new Date(),
    futureTime= new Date(
      _date[0],
      _date[1] - 1,
      _date[2],
      _date[3],
      _date[4]
    ),
    leftTime= futureTime - currentTime;

    if(leftTime > 0 && typeof func === "function") {
      return setTimeout(func, leftTime);
    }
}

executeAt takes the a string representing time (in ‘Year, Month, Date, Hour, Minute’ format) and a callback as it’s arguments. executeAt finishes immediately but sets a setTimeout to call the passed callback when the clock strikes the given time.

Now suppose, we are going to organize a conference on June, 17, 2016 at around 9:35 PM. We use executeAt function to remind our users of it five minutes before it begins.

var confNotification= executeAt("2016, 6, 17, 21, 30", function notifyUsers() {
    var notify= new Notification("Conference Time!", {
        body: "DSConf is going to start online in a few minutes. Get ready!"
    });
});

Tada! we’ve awesomely scheduled a reminder for the live users on our site.

Further Reading

JavaScript Callbacks explained through Minions (must read if you’ve come this far)

Conclusion

Most of the important things in JavaScript are done through callbacks. Using APIs requires a firm understanding how callbacks work. You can choose to flavour even your own APIs with callbacks to provide your users with stress-free interface.

Do share your thoughts with us in the comments section below.

Share on Facebook Tweet on Twitter Post on Google+ Buffer