Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tzmartin/1026916 to your computer and use it in GitHub Desktop.
Save tzmartin/1026916 to your computer and use it in GitHub Desktop.
On JavaScript callbacks, program flow control and the functional style.

One of the first things a newcomer to JavaScript will notice when confronted with the modern style is the prevalence of function objects passed as parameters to other functions, or callbacks. What is not immediately apparent is why this higher-order function style needed. In short, functional arguments give us a way to delay the execution of a block of code. This kind of program flow control means we can approximate some powerful abstraction techniques usually reserved for macros in other languages.

Here's a typical callback example you'll find on the client-side, attaching a click event handler to a DOM element:

var callback = function (evt) { console.log("Element clicked!"); };
element.addEventListener('click', callback);

Here we've created a variable to hold our callback function. Next we have our DOM element listen for click events and, when it has one, execute our function. The callback function has been taken out of the normal program flow to be run at another time, or in this case, when the element is clicked at an undetermined time in the future.

Before we design our own higher-order functions we need to look at a regular function and it's execution order:

function add (x, y) {
  return (x + y);
}

add(1 + 2, 3 + 4); //-> 10

Here is a simple addition function that returns the sum of two arguments. What's interesting is its invocation, the arguments are first evaluated before being passed: add(1 + 2, 3 + 4) => add(3, 7) => 10. We can be assured that any expressions in these slots will be evaluated and their value funneled into the function.

Now let's say we wanted to write the function if_not that takes 3 arguments: if the first argument is false then we run the second argument, and if the first argument is true, then we run the third. This is basically a reverse if control. We'll work backwards so let's see what the invocation of this function would look like:

if_not(false, console.log("Run me!"), console.log("Missiles launched!"));

Looking at the previous example and the evaluation order, can you see the problem? We only want to call the second argument, but before if_not is called, both are evaluated—launching the missiles! However, if we write the if_not function to take two functions, then we can decide which to run:

function if_not (condition, callback1, callback2) {
  if (condition === false) {
    callback1();
  } else {
    callback2();
  }
}

This way only the second argument is executed, the missiles are kept safely at bay:

if_not(false, function () { console.log("Run me!"); },
              function () { console.log("Missiles launched!"); });
//-> "Run me!"

Another handy technique is running a block of code with an undetermined value, especially when waiting on the user:

function with_name (name, callback) {
  var lang = (name === "billy") ? "javascript" : "how should i know?";
  callback(name, lang);
}

function likes_lang (name, language) {
  console.log(name + " likes " + language);
}

with_name("billy", likes_lang);  //-> "billy likes javascript"
with_name("ringo", likes_lang);  //-> "ringo likes how should i know?"

What you're starting to see is the beauty of using a functional style. By working with discreet chunks of code with known inputs and outputs, we can begin to layer them on top of each other to form powerful and flexible abstractions, essentially building up a vocabulary of functions. But the best part is debugging! When you run into a problem you generally just do a stack trace to find the offending code based on what arguments have being passed. There's no environmental state setup needed because—ideally—all the required parts reside in the function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment