Understanding JavaScript Callbacks

Unlocking the Asynchronous Powers of JavaScript

When I got started working with Node.js and Express, one of the first things I had to really wrap my head around was JavaScript callbacks. This is a powerful functionality built into the language that allows you to defer action until a desired event occurs, while proceeding on with other activities.

The truth is, I’ve been using callbacks for a long time, I just didn’t really realize it. I work with jQuery constantly, and it’s designed with callbacks in mind, passing anonymous functions via its built-in methods to be triggered when certain events occur, for example. Still, I wanted to move beyond “I know this works” to “I know how this works” both with jQuery and JavaScript, so I dug out my copy of JavaScript: The Good Parts, did some reading, hit the web, did some more reading, and then wrote a few simple experiments.

I thought I’d write up a little tutorial to help folks who are new to the concept get started. Hope it’s useful!

Life Without Callbacks

Let’s start with a very simple script that uses no callbacks. First we’ll establish a super-quick HTML structure to output results to:

<html>
    <body>
        <p id="output">
        </p>
    </body>
</html>

And then we’ll add a bit of JavaScript:

document.getElementById('output').innerHTML += ('starting ...');
document.getElementById('output').innerHTML += ('continuing ...');
document.getElementById('output').innerHTML += ('ending!');

Go ahead and run it for yourself. This is tremendously boring code, and it could also be shortened if we were working with jQuery, but it gets the point across. Things in JavaScript typically execute sequentially. Let’s really hammer this home by putting the ending in the middle:

document.getElementById('output').innerHTML += ('starting ...');
document.getElementById('output').innerHTML += ('ending!');
document.getElementById('output').innerHTML += ('continuing ...');

So, great … as you can see when you run this, it prints “ending!” before “continuing …” pretty much exactly as expected. And no good. We don’t want to end in the middle. We want to end at the end.

So let’s do that.

setTimeout() – My First Callback

JavaScript has a function for delayed execution of commands. You give it a command to run, and the number of milliseconds to wait before running it. It’s handy for a variety of reasons, but its value here is that it inherently uses callback functionality to work its magic. Here’s our new code:

document.getElementById('output').innerHTML += ('starting ...');
var myTimer = window.setTimeout(function() {
    document.getElementById('output').innerHTML += ('ending!');
}, 500);
document.getElementById('output').innerHTML += ('continuing ...');

Check that out. Now we’re back to having the right order in our output, even though the code’s not sequential, because we’re waiting half a second before executing an anonymous function that writes our “ending!” string. Finding the anonymous function thing confusing? You could also write the code like this:

document.getElementById('output').innerHTML += ('starting ...');

// Wait half a second before firing the writeEnding() function
var myTimer = window.setTimeout(writeEnding, 500);
document.getElementById('output').innerHTML += ('continuing ...');

// Define the Write Ending Function
function writeEnding() {
    // Write "ending!"
    document.getElementById('output').innerHTML += ('ending!');
}

Same exact thing, but now the function’s not anonymous because we declared it at the bottom. We’re still calling it with setTimeout, though, after 500 milliseconds. This is nice for re-use, obviously, and is also often handy for keeping code readable (which is important, especially if you work with multiple developers).

Creating Our Own Callbacks

We’ve established that setTimeout uses a callback to enact a delayed execution of a function, but how do we write one of our own? Easy! Like this:

// Call our main function. Pass it a URI and a callback function
getData('http://fakedomain1234.com/userlist', writeData);

// Write some stuff to the p tag
document.getElementById('output').innerHTML += 'show this before data ...';

// Define our main function
function getData(dataURI, callback) {

    // Normally you would actually connect to a server here.
    // We're just going to simulate a 3-second delay.
    var timer = setTimeout(function () {

    	// Here's some data which we're pretending came from dataURI
        var dataArray = [123, 456, 789, 012, 345, 678];

    	// run our callback function
        callback(dataArray);

    }, 3000);
}

function writeData(myData) {
    document.getElementById('output').innerHTML += myData;
}

If you run this in jsFiddle, you’ll see it behaves just as we want it to: even though the getData function is the first thing called, and takes three seconds to run, the script continues right along. When the three seconds are up and getData responds with data, then writeData fires and writes the data.

Obviously we’re still using a setTimeout here, but that’s not required for callbacks, it’s just hard to simulate long delays in a tutorial setting without it. Here is a non-working example that uses a fake data call, so you can get an idea for what it would look like:

getData('http://fakedomain1234.com/userlist', writeData);

document.getElementById('output').innerHTML += "show this before data ...";

function getData(dataURI, callback) {
    var myData = getSomeData(); // fake function
    callback(myData);
}

function writeData(myData) {
    document.getElementById('output').innerHTML += myData;
}

The key thing to understand here is that you don’t have to be using AJAX. Merely by adding callback functionality to your JavaScript, it automatically becomes asynchronous. This has far-reaching value, particularly when you’re working with Node.js.

A Brief Caveat

It’s important to note that callbacks do not have to be asynchronous. You can use them just as easily in sequential code. In fact, it’s best not to rely on callbacks for a delay, unless you’re manually controlling that delay. For example, let’s use our code above but without forcing a delay:

getData('http://fakedomain1234.com/userlist', writeData);

document.getElementById('output').innerHTML += "show this before data ...";

function getData(dataURI, callback) {
    var dataArray = [123, 456, 789, 012, 345, 678];
    callback(dataArray);
}

function writeData(myData) {
    document.getElementById('output').innerHTML += myData;
}

Go ahead and run that. Whoops … it’s not behaving right. That’s because since we’re not forcing a delay with setTimeout, or doing something that would naturally cause a delay (like connecting to a DB and snagging a whole big chunk of data), the function is executing basically instantaneously, and calling the callback.

This is why it’s better to rely on callbacks for events than for actual timing purposes. If you really want to introduce specific, milisecond-based actions to your code, use setTimout and its brother setInterval (which will repeatedly fire a callback function rather than just doing so once). Use callbacks when you want to fire something off, move on to other things, and then react when what you originally started is finished.

OK, callbacks are nifty. Why do I care?

If you’re working with Node, you care because the entire platform is built around the callback concept. Everything Node does is asynchronous. You fire a command, and move on to other commands, and when your first command is finished executing it calls a callback. If you don’t code your Node apps this way, you’re basically nullifying one of the two biggest advantages Node has (those two being: its async nature, and the fact that it’s all JavaScript and thus nearly every single web developer can code for it).

If you lace your Node app with blocking code – code that doesn’t use callbacks and thus forces everything else to sit and wait as it executes – your app is going to be slow and clunky. This is exactly what you don’t want, so don’t do it! Make sure you’re using callbacks for pretty much everything.

In addition to helping with node, understanding callbacks (and ‘event based programming’) will make interacting with frameworks like jQuery much more clear. Let’s take a look at a very simple, very common jQuery action:

$('body').on('click', 'p#output', function() {
    alert('You clicked the output paragraph.');
});

Here’s that super-exciting script in jsFiddle just for fun. You see the callback happening here, right? It’s the anonymous function that triggers the alert. This is essentially how all jQuery events work: listen for an event, and when it happens, run the callback function, and in the meantime don’t hold up any other JS on the page. If you want a named function, you could just as easily do:

$('body').on('click', 'p#output', showAlert);

function showAlert() {
    alert('You clicked the output paragraph');
}

Same deal either way, whether the callback function is declared or anonymized.

That’s really all there is to callbacks. They’re really simple once you get used to them. JavaScript: The Good Parts only gives them about a page and a half total because there’s not that much to talk about. The most important thing is just to remember to use them. Your code will be speedier, you’ll be better-prepared to take advantage of event-based frameworks, and you’ll have an easier time understanding and working with things like the MEAN stack (Mongo, Express, Angular, Node).

Hope this helps! Any questions? Drop me a line or comment in the discussion below.

Published on Codingpedia.org with the permission of Christopher Buecheler – source UNDERSTANDING JAVASCRIPT CALLBACKS from http://cwbuecheler.com/

Johannes Brodwall

Christopher Buecheler</strong>

Christopher Buecheler has been designing and developing web pages professionally since 1997. He's worked as the principal front-end developer for four startups: GameSpy Industries, OkCupid, CrispyGamer, and GoldenSpear. A self-taught programmer, he understands the value of a good tutorial, and has tried to give a few back to the community. In addition to web work, he's independently published four novels and is currently working with his agent to find a publisher for his fifth. He also writes cocktail articles for Primer Magazine and runs a cocktail blog at DrinkShouts.com. He lives in Providence, Rhode Island, with his amazing French wife and their two cats.

 

 

Free Programming Books are now on Codingmarks.org

We're happy to announce that we've reached and surpassed our goal of 1 Mb #codingmarks, by importing the books from the Free-Programming-Books project Continue reading