Callbacks

This is how callback functions work…

function iHaveACallBack(cb) {
  // Do some stuff
  // Then invoke the callback. Optionally pass in whatever args you expect to be useful.
  cb(importantArgs);
}

When I first started using callbacks in JavaScript, I was unsure of how they worked. In particular, the arguments that magically appeared in these functions were mysterious. I’d be like…

myFancyElement.addEventListener('click', function(e) {

  console.log(e); // <- What the?... How the hell does my anonymous function know about e?!

  // Whatever, moving on.
  e.preventDefault();

  // etc, etc.
});

If this is confusing, don’t let it discourage you. Once you take a minute to add a callback to one of your own functions, it should become clearer. To create one, just add the callback as a parameter to your function signature, then from inside your function, invoke the callback. Of course, you can name it whatever you want, just make sure you invoke it with an open and close parenthesis and pass in whatever arguments are necessary to make it useful.

Let’s write a function with a callback.

function getPerson(person, cb) { // <- Our callback is named 'cb'

  // Do stuff here and prepare any arguments to pass to the callback below
  let name = person.name;

  cb(name); // <- Here we will invoke the callback and pass in the 'name' value we get from the 'person' argument
}

Now we can create a couple objects and use this function with whatever callback we want, be it anonymous or not. Here’s what it looks like with an anonymous function.

let person1 = { name: 'Nate' };
let person2 = { name: 'Suzanne' };

getPerson(person1, function(name) {
  alert( 'Hello. My name is ' + name + '!' );
});

getPerson(person2, function(name) {
  console.log( 'Hi there. My name is ' + name + '!' );
});

// Hello. My name is Nate!
// Hi there. My name is Suzanne!

In most cases, I prefer to declare callbacks separately. It keeps things a little cleaner. Note that you will need to define the correct parameters in the signature of your callback function. Then just pass a reference to the callback as the second argument (in this case). Also note that we’re passing a reference, and NOT invoking the callback. Don’t confuse the parenthesis that surround the parameters in the anonymous version with the parenthesis that invoke. It’s the job of the parent function to invoke the callback.

function fetchPerson(person, cb) {

  // Do stuff here and prepare any arguments to pass to the callback below
  let salutation = person.salutation;
  let name = person.name;

  cb(salutation, name); // <- These arguments will match the parameters of the function below

}

function greet(salutation, name) {
  console.log(salutation + 'My name is ' + name + '!');
}

let person1 = {
  name: 'Nate',
  salutation: 'Hello.'
};

let person2 = {
  name: 'Suzanne',
  salutation: 'Hi there.'
};

fetchPerson(person1, greet);
fetchPerson(person2, greet);

// Hello. My name is Nate!
// Hi there. My name is Suzanne!

Just to be clear, although the greet function uses the exact same variable names as the fetchPerson function, that is not a requirement. If we want to use the arguments passed to our callbacks, then we need to have those variables in our function signature, but they could be named anything. Obviously, you always want to use meaningful variable names, but it could also be written as such…

function greet(plop, boink) {
  console.log(plop + 'My name is ' + boink + '!');
}

…and that would still work—but don’t do that.

So there you have it. Go forth and prosper.