Asynchronous "Hello, world!"

For me, one of the toughest things about learning JavaScript is dealing with asynchronous code via callbacks.

Here’s a trivial “Hello, world!”:

var hello_world_string = '';
function storeHello() { hello_world_string += 'Hello, '; }
function storeWorld() { hello_world_string += 'world!'; }
storeHello();
storeWorld();
console.log(hello_world_string);

This obviously outputs “Hello, world!” to console.log. But this is completely deterministic, procedural code, and that’s not always the case in Javascript.

If you’re making an AJAX call from a client/browser to a server or pulling a value out of an IndexedDB database, you must process the results via a callback that fires after an unknowable period of time passes. To simulate this behavior, let’s change storeHello():

function storeHello() {
  setTimeout(function () {
    hello_world_string += 'Hello, ';
  }, 500);
}

This adds an artificial half-second delay before what we really want storeHello() to do happens. What now happens?

You might suspect console.log now spits out “world!Hello, ” but it actually spits out just “world!” Why? Because console.log() fires before the setTimeout() concludes and calls its callback. So the string is printed out without ‘Hello, ’. hello_world_string eventually gets set to “world!Hello, ” but only after it has already printed. Code execution doesn’t stop half a second when storeHello() is called and its setTimeout() sets a half-second delay. Code execution continues immediately with the next command, storeWorld(), and then the next, console.log().

If you really need the string components to print in the correct order after all functions have finished processing, you need to do additional work. One way to guarantee the correct order is by calling storeWorld() inside storeHello()’s callback function and calling console.log() inside storeWorld():

var hello_world_string = '';

function storeHello() {
  setTimeout(function () {
    hello_world_string += 'Hello, ';
    storeWorld();
  }, 500);
}

function storeWorld() {
  setTimeout(function () {
    hello_world_string += 'world!';
    console.log(hello_world_string);
  }, 500);
}

storeHello();

This correctly prints (after a 1-second delay) “Hello, world!” to the console.

You could also pass a callback into storeHello() that would get passed through to storeWorld() and then invoked after hello_world_string was complete:

var hello_world_string = '';

function storeHello(cb) {
  setTimeout(function () {
    hello_world_string += 'Hello, ';
    storeWorld(cb);
  }, 500);
}

function storeWorld(cb) {
  setTimeout(function () {
    hello_world_string += 'world!';
    cb();
  }, 500);
}

storeHello(function () {console.log(hello_world_string);});

Of course, you could name this anonymous callback:

var log_hello_to_console = function () { console.log(hello_world_string); };

and then invoke it as:

storeHello(log_hello_to_console);

Injecting the final step of the process as a callback gives you run-time flexibility to change what you do when the string is ready for prime time. You might instead want to pass in:

var log_hello_to_console = function () { console.log("*** " + hello_world_string + " ***"); };

or

var log_hello_to_console = function () { $('#greeting').html(hello_world_string); };

An alternative to placing callbacks inside callbacks would be to create an object responsible for gathering the string components, joining them in the proper order, and doing something with the full string (e.g., logging it to the console) after it received the final component. This approach would likely be several times faster because components can be gathered simultaneously (in parallel), rather than sequentially (in series) as happens when callbacks invoke callbacks.

Posted by James on Friday, March 22, 2013