Much Ado About Callbacks
Have you read or seen Shakespeare’s ‘Much Ado About Nothing’? It’s a pretty funny play. The short story is that two characters have a thing and want to get hitched, except a rumor gets out that the girl has been hooking up with this other dude (not her boyfriend). Chaos ensues because it was back in the day and that sort of behavior was unseemly. But in the end it’s all ok because it was just a rumor, and didn’t really happen.
Much Ado About Nothing.
That pretty much describes my relationship with callbacks up until recently. I was really intimidated by the idea of them, and if I saw them I tried to find some other way of accomplishing my goal without using callbacks. However this mentality – appealing as it may be in the moment – is not conducive to growing and learning.
Recently I was working on a game in Phaser. (I’ll post more about it soon.) I wanted to call certain functions upon specific key presses. For instance, when the user pressed the spacebar, I wanted their character to jump. I tried really hard to find a way to do this without using a callback, and I came up empty. So it was time to me to face my fear and figure out how to work with a callback.
Turns out in looking at them more and really focusing, I was really getting intimidated and worked up over nothing.
So what is a callback exactly? Well, a callback is a function that is passed in as a parameter. It might look this:
spaceKey.onDown.add(jumpUp, this);
You can see more code here.
So basically what this is saying is “when the user presses spaceKey, call the jumpUp function.” In this case, the jumpOn function is only called when the player presses the spacebar.
Here’s another example.
module.exports = function getExtName(dirName, ext, callback) {
var result = [];
fs.readdir(dirName, function(err, files) {
if(err) {
console.log(err);
return callback(err);
} else {
for(i = 0; i < files.length; i++) {
if(path.extname(files[i]) == '.' + ext) {
result.push(files[i]);
}
}
return callback(null, result);
}
});
};
And here’s the function being passed in…
function outputToConsole(err, data) {
if(err) {
console.log(err);
} else {
console.log(data.join('\n'));
}
}
mod(filePath, extName, outputToConsole);
You can see more of this code here.
As you can see above the callback function is returning data, whether it be the error, or the data that the function is manipulating and then returning. This is done as a callback to the fs.readdir function because we need the data to be read in before we do any manipulation on it. Otherwise the outputToConsole function would likely run before the data was even read in. That would in turn mean that data would be undefined and the function wouldn’t work.
Notice that in these functions when I pass in callback, I do not pass it in with parenthesis. This allows the function to be passed in, but not called until I choose for it to be called.
The nature of JavaScript is that it reads your code from left to right, top to bottom. It runs any functions in the order they are called. But there are cases (like the examples above) where you don’t want the function to be called when it is read. You’ll only want it to be called when something else happens…like when you get the correct user input, or you want it to wait until after your data has been read in.
This is why callbacks are so handy!
Think also about if you are waiting on the user to give your program data. Any manipulation of that data would have to wait until the data has been typed in. (Otherwise the variable holding your data would be undefined, right?) This is another good example of when you might want to use a callback.
TL;DR
Callbacks aren’t as scary as they seem. Write up your function and pass it in as a parameter (without parenthesis) and then you can call it only when you actually want it to be used. This allows your other functions to continue running while you wait for user feedback, or to access the information you need for your callback.