JavaScript Callback Functions explained in their simplest form.
Tags: beginners, javascript, programming
Published: 2022-12-14 16:21:09

 _JavaScript Callback functions are one of the main features of the language that allow you to implement some asynchronous programming patterns, and is very common in the language, as it is an event-driven language. 
A lot of guides, tutorials, and how-to videos out there attempt to explain how this stuff works. The issue I take with them is that they do not explain callbacks in their simplest possible form. That's why I'm writing this guide. I'm going to break down each step, explaining the process of the callback function in it's most basic form so that you gain a fundamental understanding of how to use them. This guide may be kind of lengthy, but that's because I'm not holding any punches or skipping any details.
Yes, this topic is confusing. No, you're not bad at programming or whatever if it doesn't make sense. There is no shortage of ignorant developers out there that have full-time, high paying salaries that don't know how callback functions actually work. Don't feel bad if you're having trouble with this concept.
With that out of the way, lets get into it._

First off, lets take a look at a normal function.

```js
function greet(name) {
  console.log("Hello, " + name + "!");
}
```
Here, we define a function with one parameter. When this function is called, it's going to take that parameter as an argument, and print it to the console. `greet("User")` will return `Hello, User!`. If you're this far in your programming journey, this will hopefully make sense to you.
In the function definition, we give it `name` just as a placeholder, and when you CALL the function, the compiler takes the argument you give it, and fits it into that `name` placeholder. So when the function runs, any instance of `name` will be "filled" with the argument you provided. In this case, we used the string of "User".

Now lets talk about what you clicked onto this article for.
But before we get into the weeds, let's get the most concrete definition of a Callback Function.

What is a Callback Function?

> In JavaScript, a callback function is a function that is passed as an argument to another function and is executed after some operation has been completed.

Thanks to ChatGPT for the simple and concise explanation.
Now, lets write a callback function solely based on this definition.
```js
function doSomething(callback) {
  // Perform a set of operations here...
  // Pull data from an API,
  // Validate data, etc
  callback();
}

function handleComplete() {
  console.log("Completed!")
}

doSomething(handleComplete);
```
Alright, now we have two function definitions here, followed by a single function call. 
The first function takes one argument, and then calls that argument as a function at the end of the function itself. This is the key concept in understanding callbacks. Even though we tell `doSomething` in its definition that we will be passing in an argument, that argument can be a function. However, don't make the mistake of writing it like this: 
```js
 function doSomething(callback()) { ... }
 ```
The key difference here, is that writing it like this tells the compiler to run a function called `callback()`, which does not exist. At least, not in this snippet of code. Instead, remember that this parameter is just a placeholder. 

Let's take a step back and look at when we call the `doSomething` function. 
We know that `doSomething` is going to take a single argument. We pass in `handleComplete`, but without the parentheses. Why? Because we aren't doing a function call inside of a function call. Doing that would look like this:

`doSomething(handleComplete())`

What we are doing is telling the compiler that we want `doSomething` to hold onto an argument called `handleComplete`. Then the `doSomething` function is going to run. Then, it's going to get to the `callback()` function, where it "plugs in" your argument's name, and THIS is what calls that function. `doSomething` does everything it needs to, then pulls that argument and runs that as a function. 
Now, if you do not have a function defined in your code called `handleComplete`, you will get an error that `handleComplete` is not a function. It's the same thing as trying to call a variable that holds a string as a function, like this:
```js
let name = "Dustin"
name()
> TypeError: name is not a function
```

Now, we're going to turn it up a notch by introducing a callback function that itself takes arguments. So, lets write out some code that illustrates this pattern.

```js
const itemsArray = [1, 2, 3, 4, 5]

function doThing(items, callback) {
  // Doing stuff
  callback(items);
}

function justArray(items) {
  // Now the operations are finished
  console.log(items)
}

function addArray(arr) {
  // Do some arbitrary stuff with an array
  arr.push(6, 7, 8)
  console.log(arr)
}

doThing(itemsArray, justArray)
doThing(itemsArray, addArray)
```

 Let's talk about how this works. So we obviously have a couple of functions here, just to help illustrate the point that you can plug in functions as a callback, so long as the argument name is a valid function name.
 
 The `doThing` function takes in some data as the first argument, and then as the second parameter, a function. So, even though the second parameter is where the magic happens (the reason it's a callback function), we know it's going to be a function, but the parameter ITSELF doesn't necessarily indicate that, because it's not written as `doThing(items, callback())`. Notice in the actual code, the second parameter has no parentheses. It's just a normal parameter. However, INSIDE the function, we see that this parameter is getting called AS A FUNCTION. This is where the compiler says "Remember that parameter we specified earlier? Now call that as a function." And at runtime, or when the function is called, it's going to take that parameter, and call it as a function, passing in the argument (which in this case is just an array of numbers). 
 
 So now, look at when the callback function is being called, near the bottom, you'll notice that we aren't calling that outside function with an argument. But when we defined it, we told it that it's going to take an argument. So what gives? Well, when we call `doThing` and provide the two arguments, we're using that array as the first argument, but the second argument is the NAME of a function that's been defined, but we aren't calling it here. It's called when the callback function runs, and that argument that holds the function, `justArray` or `addArray`, matches the name of the parameter. Remember, that the compiler calls that parameter name with parentheses and THAT is what calls it. 
 
 Now, we're going to back up and look at the main `doThing` function again. Let's say that the function we're passing in is `addArray`, so we'd do so by writing `doThing(itemsArray, addArray)`. At this point in time, `addArray` is just an argument name. But when the callback function reaches the point in the code where it says to do `callback()` WITH THE PARENTHESES, the compiler says "Oh, that parameter is going to be called as a function" and runs it. If you try to run this argument without it being an actual function, the compiler will error out and tell you that the argument you tried to use is not a function. So, in essence, this is what running the callback function looks like.

So that's how they work. Before I wrap up here, let's look at a practical example of how you'll use a callback function in the wild.
```js
// Get the element on which to attach the event listener
const element = document.getElementById("my-element");

// Create the event listener function
function eventListener() {
  // This code will run when the specified event occurs
  alert("The event occurred!");
};

// Attach the event listener to the element
element.addEventListener("click", eventListener);
```

This is one of the most common and perhaps most basic things you'll use callback functions for. In fact, I used eventListeners for awhile before actually knowing how they worked, or that they were even considered callback functions. First, we grab an element from the DOM that we can add a click to, then we define what we want that click to actually do. In this case, it's going to make your browser open an alert box that says `The event occurred!`. 
The important part I want to highlight is the function call itself, where we tell the compiler to add an event listener to the element we grabbed earlier, tell it that it's going to call a function called `eventListener` when that element is clicked. 
There's some JavaScript stuff going on under the hood here, but the important take-away is that we tell `.addEventListener` that `"click"` is the first argument, and the second one is `eventListener`, the function. After the element is clicked and it does its "under the hood" stuff, the `eventListener` function is going to be called, which again, just alerts your browser. 

Another common use of callbacks is using them as anonymous functions.
```js
  element.addEventListener("click", () => {
    console.log("I just got clicked.")
}
```
This is pretty useful, specifically for eventListeners just because you can specify one or multiple functions to be run when you click a button and you don't have to define an outside function for it to run. The concept is the same however. The main function `.addEventListener` does its thing, then runs your function as a callback after it's done.

That is about all I have to explain for understanding callback functions. It's a difficult topic to cover, but I feel like understanding this fundamental building block as not just a web developer, but any kind of programmer will benefit from understanding this pattern. Hopefully this article helped you understand callback functions.