Javascript Callbacks

Callbacks are found all over the place in JavaScript. A callback is simply a function that delays its execution until the time is just right for it to execute.

Synchronous vs. Asynchronous JavaScript

You'll often hear about a concept called synchronous and asynchronous programming/programs/code. When a piece of code is synchronous, it means that it will be read from top to bottom, line by line, and wait until every line is finished before it will move on to the next one. Asynchronous means that whenever you run into a line that will take awhile to complete, you allow the JavaScript program to continue to read and execute other lines of code until the delayed line of code completes, then you come back and continue where you left off.

Here's a very basic example: you want to wait for 5 seconds after a user loads a web page before displaying an alert to them. But you don't want to completely stop all the other JavaScript from being read into memory until the alert is displayed. If you did, your user wouldn't be able to benefit from the JavaScript on your page until the 5 seconds had passed. And 5 seconds in the computer world is a very long time.

If you just executed the function as soon as the page loads, you wouldn't be able to wait 5 seconds, and it would just display the alert immediately:

// No callback, this sends the alert as soon as the web browser's 
// JavaScript engine reads that it should execute the function
function alertUser() {
    alert("Welcome to the home page!");
}

alertUser();

There's a global function built right into JavaScript called setTimeout(). This function takes 2 parameters, the first one is the function it should run, and the second one is how long (in milliseconds) it should wait before running that function. The first parameter is considered a "callback function". Here's an example:

function alertUser() {
    alert("Welcome to the home page!");
}

setTimeout(alertUser, 5000);

Notice that in the code above, you don't pass alertUser() as the parameter to the function, but instead just pass the name of the function alertUser (without the executing parentheses). If you use the parentheses, it will try to execute the function on the spot, which is the opposite of what we want it to do. Instead, we say "wait 5,000 milliseconds, and then call back to the function named alertUser and execute it."

Callbacks with Anonymous Functions

Above we passed in a named function to setTimeout. It's also very common to pass anonymous functions as the callback function instead.

Note: if you're planning on doing unit testing, you probably want to avoid using too many anonymous functions as it makes it more difficult to test.

Since passing in a named function without the parentheses is like passing in the text of the function itself (rather than just executing it), you can write your function, without a name, in the place of where the function should go as a callback. The following is from the same problem we solved above, but using an anonymous function instead:

setTimeout(function () {
    alert("Welcome to the home page!");
}, 5000);

Don't be thrown off by the extra lines of code - this is essentially the exact same thing as what we had above.

As stated above, if you need to use this function multiple times, or if you're planning on adding unit tests to your code, you're better off defining the function as a named function, then passing the name of the function in as the callback rather than using an anonymous function.

Another Example - HTTP requests

If you haven't learned about HTTP yet, don't worry too much about the semantics of it right now. The only thing you need to know is that when you ask another computer somewhere else in the world for some information, it can sometimes take longer than the near-instantaneous speed in which JavaScript reads code. If you figure in slow internet speeds into the mix, it may take upwards of 10-30 seconds (practically an eternity compared to normal computing speeds) for your request for data or resources (photos, videos, etc.) to return with the data you want.

In these instances, callbacks are a go-to way of waiting until your request finishes in order to execute the function you want. For example, imagine you have a user of your web site named "Sally" who is logging into her account on your website, and you want to display a message that says "Welcome, Sally" on the page after the login is successful.

Your web application doesn't know the person logging in is named Sally until after the data comes back from the server. So we need to wait for the response from the server before we can display the message with the user's name.

In jQuery, when you make an HTTP request to a server, you also need to provide at least one callback function. This callback function will only execute when the request comes back successfully with all the data you asked for. This way, your user doesn't get something showing up on the page that says "Welcome, ", with no name included.

Common usage - 3rd Party Libraries and APIs

When you're reading the documentation for 3rd party JavaScript libraries and APIs online, you'll frequently see that you need to provide a callback function to their method. For example, in Mongoose (a third-party javascript library that helps you interact with the Mongo Database), you'll see code like this:

User.findById("123456789abcdef", function(err, user) {
    if (err) {
        console.log(err);
    }
    console.log(user);
});

The first parameter to the findById method is the id of the object you're trying to find. The second parameter is a callback function. In this instance, we've chosen to write it in-line as an anonymous function. The documentation explains that the findById method will pass an error, if there was one, as the first parameter. If there's isn't one, it will pass the user object it found to the second parameter of the callback function. So inside the body of the function, we can assume that the callback function is only going to get called once the interaction with the database has completed in one way or another (successfully or not).

Writing our own callback functions

When we write a higher-order function (a function that accepts a callback function as a parameter), we accept a parameter that we expect will be a function, and then we direct exactly when we want to execute that callback function.