Callback Functions: Confusing, but necessary
Before we start exploring the different built-in JavaScript functions, you need at least a general understanding of callback functions.
No, these are not a different type of writing a function. They represent a different way of using a function.
Here is some really confusing code that I hope you will have a basic understanding of 5 minutes from now.
function myCallback(someNumber) {
return someNumber * 2;
}
function mainFunction(randomNumber, shouldCall, callback) {
let result = randomNumber;
if (shouldCall) {
result = callback(randomNumber);
}
return result;
}
mainFunction(20, true, myCallback);
This also could have been simplified to the following (does the same exact thing):
function mainFunction(randomNumber, shouldCall, callback) {
let result = randomNumber;
if (shouldCall) {
result = callback(randomNumber);
}
return result;
}
mainFunction(20, true, (num) => num * 2);
Unfortunately for the beginner, the second block of code is what you'll see most often because it is more succinct.
Let's walk through the first code block with some comments.
function myCallback(someNumber) {
return someNumber * 2;
}
function mainFunction(randomNumber, shouldCall, callback) {
let result = randomNumber; // in this example result === 20
// In this example, shouldCall is `true`, so we do reach the callback
if (shouldCall) {
// In this example, `callback` represents `myCallback` from above
result = callback(randomNumber);
}
// Since `result` was re-assigned by the callback function, returns 40
return result;
}
mainFunction(20, true, myCallback); // returns 40
We could have gotten the same result by just calling myCallback
.
myCallback(20); // returns 40
There is nothing special about myCallback
. It is just a function, but instead of calling this function separately, we can ask mainFunction
to do it for us! Zooming in on result = callback(randomNumber)
, you can see that we are taking the value of randomNumber
, which is 20
in this case and passing it in as an argument to callback
. What is callback
? It's the function we pass in as an argument.
So let's take the function we defined just a second ago, myCallback
, and pass it into mainFunction
as an argument!
mainFunction(20, true, myCallback);
And of course, you don't have to define myCallback
as a separate function. You could do it as an anonymous function OR an arrow function. All of these produce the same result.
function myCallback(someNumber) {
return someNumber * 2;
}
function mainFunction(randomNumber, shouldCall, callback) {
let result = randomNumber;
if (shouldCall) {
result = callback(randomNumber);
}
return result;
}
/**
* Different methods of using callbacks below 👇
*/
// Using pre-defined function as a callback
mainFunction(20, true, myCallback);
// Using anonymous function as a callback
mainFunction(20, true, function (num) {
return num * 2;
});
// Using an arrow function as a callback
mainFunction(20, true, (num) => {
return num * 2;
});
// Using an arrow function with abbreviated notation
mainFunction(20, true, (num) => num * 2);
// Using an arrow function with even MORE abbreviation
mainFunction(20, true, (num) => num * 2);
So... What's the point of a callback?
There are two advantages:
- Reusability of functions
- Asynchronous programming
Callbacks enable reusability
Let's look at a built-in JavaScript function called arr.map()
. You can find the documentation here, and I encourage you to try and figure it out before we start talking about it.
const myArray = [2, 4, 6, 8];
// itemFromArray represents a single value from the array above such as `2`
// Hint: the arr.map() function is similar to looping through an array like we did in the challenge problems from lesson 5
function myCustomMapOperation(itemFromArray) {
return itemFromArray * 2;
}
const newArray = myArray.map(myCustomMapOperation);
console.log(newArray); // [4, 8, 12, 16]
In this example, I am passing myCustomMapOperation
as my "callback" function into the built-in arr.map()
JavaScript function. This custom callback function that I wrote will double the values in an array.
But what if my array was filled with string values and I wanted to make a new array that only contains the first letter of each string? Don't I have to go search for another built-in JavaScript function to do this?
NO!!
Callback functions make things reusable. Since we as the developers are responsible for defining what that callback function will do, we can reuse the arr.map()
function for a variety of purposes. Here's how I would implement the idea I just presented.
const myArray = ["Hello", "world", "my", "name", "is", "Zach"];
function myCustomMapOperation(itemFromArray) {
// You can grab characters from a string value just like you can
return itemFromArray[0];
}
const newArray = myArray.map(myCustomMapOperation);
console.log(newArray); // ["H", "w", "m", "n", "i", "Z"];
Asynchronous Programming: Callbacks, Promises, and async-await
Yep, I said it. "Asynchronous" is a word that you're going to learn to love and hate at the same time.
In programming, not all operations happen near-instantaneous like the code we have been writing does. For example, what happens when a web app needs to fetch some data from a database and the internet is slow that day? This operation is going to take a couple of seconds depending on the latency of your internet.
You might say–well then, let's just wait until it is done before executing any more code?
Wrong answer, but a good thought. We cannot just wait for it to happen because in many apps, there are hundreds of these operations happening at once and if we waited for each of them, our webpage would take several minutes to load. Nobody wants that.
We will not be diving into any code in this lesson, but there will be a future lesson solely devoted to covering asynchronous programming because it is a large topic and can get rather confusing.