Callbacks are frequently mentioned in the context of functions and events, but can also be one of the more difficult aspects of JavaScript to understand. So what are callbacks, and how do they work?
You’re probably already familiar with callbacks; for example, they frequently appear inside event listeners:
button.addEventListener("click", function() {
… do something
})
See the function()
reference? That’s an example of a callback.
Why Are Callbacks Necessary?
JavaScript is event-driven: it executes in the order that events happen and results are produced, not necessarily in the order that code appears. This means that code like the following:
function first() {
…do something that takes a lot of calculation…
console.log("First");
}
first();
Will print First in the console after the calculations are complete.
I’ll change this function so that it is actually slowed before it gets to print out First, and add a second one that executes almost instantly:
function first() {
setTimeout(function() { console.log("First"); }, 1000);
}
function second(){
console.log("Second");
}
first();
second();
The result in the console will be produced in a different order than what we might expect:
Second
First
Why? During exection, JavaScript finds the first()
function and starts to run that. Then, without pausing, it goes ahead and finds the second function and runs that too. JavaScript never stops, and is always looking for more work to do: that’s one of the reasons it runs so fast in modern browsers.
Tidying Up Callbacks
In the case of the event listener example we started with, we wait for the click
event to complete before we execute the rest of the code. But if you’re only doing one thing, it may be better that the callback is substituted with named functions:
function makeMenu() {
… do something …
}
button.addEventListener("click", makeMenu);
This will have the same result, but abstracts the code, and makes the makeMenu
function reusable by other parts of our script. However, one potential problem of this approach is that including parameters in the function will cause it to fire immediately, without any click taking place at all:
function makeMenu(param1) {
console.log("Clicked");
}
button.addEventListener("click", makeMenu(param1));
The result in the console, without the button ever being clicked, will be:
Clicked
In other words, the JavaScript is firing the event without any action taking place. To get around this, we can go back to a formal callback:
function makeMenu(param1) {
console.log("Clicked");
}
button.addEventListener("click", function() { makeMenu(param1) } );
Alternatively, you can .bind()
the parameters:
button.addEventListener("click", function() { makeMenu.bind(null, param1) } );
I’ll talk more about .bind()
, together with advanced features of callbacks, in the next article.
Conclusion
In essence, callbacks are a way to control the order of execution in JavaScript: they’re a way to make things happen in logical order. This is also known as asynchronous JavaScript: passing a function as a parameter to say “do this in the future”. (How much in the future, or the conditions under which that future event takes place, being part of the callback and the function). In ES6, this can also be the purpose of Promises.
Callbacks appear throughout JavaScript, and are part of the language itself: window.requestAnimationFrame()
is an example of a callback that executes once the browser indicates it is ready to paint something to the screen, allowing for smooth animation.
The “run constantly” aspect of JavaScript - “listen for events all the time, and react to everything immediately” - also influences aspect of performance, especially in events that are fired frequently, such as window scrolling, resize, and use of some form inputs such as range
. I’ll address that in an article on performance throttling.
The event-driven nature of JavaScript is also controlled to some degree by closures, scope and context, which I cover in separate articles.
Photograph by Alan Szalwinski, used under a Creative Commons Attribution-NonCommercial 2.0 Generic license
Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.