Call me maybe? Callbacks for beginners
Phillip Shim
Posted on June 25, 2019
In JavaScript, functions are what are known as first-class citizens. It means they can be stored in a variable, passed as an argument to a function and returned from a function. A Callback takes full advantage of this nature because it's a function that is passed as an argument to another function and is invoked there.
Just a function
Let's take a look at this simple code snippet of console logs.
console.log("Hello There"); // Hello There
console.log("I am a tomato!"); // I am a tomato!
It's very straightforward. The above code will be executed line by line as you would expect. Let's switch this up a little bit. Because we always want to output "I am a tomato!" after "Hello There", let's automate it so that when "Hello There" is printed, "I am a tomato!" is also printed by default. The first step is to wrap each of our strings inside their own functions.
function greet() {
console.log("Hello There");
}
function introduce() {
console.log("I am a tomato!");
}
Now we will pass in 'introduce' function as a callback to the 'greet' function and invoke it inside. Make sure you only pass in function definition as a callback without (), if you append () when the function is passed in, 'introduce' will just get invoked immediately instead of waiting to be executed inside the 'greet' function.
function greet(callback) {
console.log("Hello There"); // Hello There!
callback(); // I am a tomato!
}
function introduce() {
console.log("I am a tomato!");
}
greet(introduce);
By convention, the callback functions as a parameter are literally called 'callback', often times you will see 'cb' for short.
Callbacks become even more powerful when we supplement them with arguments inside the function that executes the callback. Let's make our 'introduce' function dynamic by giving it the ability to change its name.
function greet(callback) {
console.log("Hello There"); // Hello There!
callback("cucumber"); // I am a cucumber!
}
// Remember, it's possible to store a function inside a variable
var introduce = function(name = "tomato") {
console.log(`I am a ${name}!`);
}
greet(introduce);
Reusability
Callbacks are conveniently very reusable because they are just JavaScript functions. We will add another function called 'callPhone' then run the original 'greet' function and 'callPhone' function sequentially.
function greet(callback) {
console.log("Hello There"); // Hello There!
callback("cucumber"); // I am a cucumber!
}
function callPhone(callback) {
console.log("Hello"); // Hello!
callback(); // I am a tomato!
}
var introduce = function(name = "tomato") {
console.log(`I am a ${name}!`);
}
greet(introduce);
callPhone(introduce);
Events
Callbacks are a must in an event listener. Let's give another scenario. We want to initially say 'Hello World', then as soon as a user clicks on a button, render 'I am a tomato!' on the console. How would we do this?
We need to make use of the addEventListner
method and append to the button DOM (HTML element). The addEventListener
takes in two arguments. The first argument is a type of event we want to listen for and the second argument is a callback function to execute after the specified event is triggered.
<button id="button">Who am I?</button>
const button = document.getElementById("button");
function introduce() {
console.log("I am a tomato!");
}
button.addEventListener("click", introduce); // I am a tomato!
Alternatively, you can directly insert an anonymous function as a callback instead.
const button = document.getElementById("button");
button.addEventListener("click", function() {
console.log("I am a tomato!");
}); // I am a tomato!
addEventListener
is a special method that will automatically invoke the callback action for us.
Network requests
When we make an AJAX HTTP network request to an API as known as asynchronous actions, it takes some time for our requests to go through and come back with a response. In order to retrieve the response data, a callback is used. One popular implementation of such is using jQuery's get
method. API servers will execute and supply the callback function with response data often with status to inform us if the request was successful.
// Grabs an filler array of objects
const url = "https://jsonplaceholder.typicode.com/posts";
$.get(url, function(data, status) {
console.log(data);
})
Not every API uses callback functions to supplement data, they can use promises which enable chaining of functions to handle responses. This topic is out of scope and won't be covered in this article.
Higher Order functions
The new features of ES6's higher order functions also make use of callback functions. These are built-in array methods that will produce a new array based on logic passed by a callback function. Such methods include forEach, map, filter, reduce, etc... The methods will take a callback function and provide it with the current value, index, and the entire array.
const numbers = [1,2,3];
numbers.forEach((number, index, array) => {
console.log(number); // 1, 2, 3
console.log(index); // 0, 1, 2
console.log(array); // [1,2,3], [1,2,3], [1,2,3]
})
Callback hell
One downside of a callback is its potential to get deeply nested and make it really difficult to be readable and maintainable. When it does, it's called callback hell. We will take the first example and expand it with more logic.
function greet(distractMe, introduce) {
console.log("Hello There"); // Hello There!
distractMe(name => {
introduce(name, hangUp => {
console.log("Good Bye")
})
});
}
function distractMe(callback) {
console.log("I am distracting you!!!");
callback("cucumber");
}
var introduce = function(name = "tomato", callback) {
console.log(`I am a ${name}!`);
callback();
}
greet(distractMe, introduce); // =>
// Hello There
// I am distracting you!!!
// I am a cucumber!
// Good Bye
Honestly, 'the' greet function example isn't that bad with only a few curly brackets and parentheses, but imagine it being more complex and the functions start to depend on each other!
Summary
Thank you for reading! callbacks are an essential part of JavaScript because of its versatility in:
- Events
- Reusability
- Asynchronous nature
- Higher-order functions.
Posted on June 25, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.