You know how you ask someone to call you back in a phone conversation? That’s what callback functions are. They are functions to be called later; after something has happened.
In order to grasp this in practice, one needs to understand two things
- that there is a difference between a function name
fn
and a function callfn()
, - that functions can be passed into functions as arguments, the same way that numbers, strings and arrays are passed into functions as arguments
The difference between a function name and a function call
Let’s consider the code snippet below
function fn() {
console.log("I am a sweet simple function");
}
We have defined a function fn
. That means that we have created a function and
stored it under the identifier fn
. If we log fn
to the console with
console.log(fn)
, we’ll see the code that we created. It makes sense to say
that we have gotten fuel and we have given it to fn
.
Fuel has the ability to burn, but it will not burn unless it is ignited. The
function that we have given to fn
is like fuel. It can be executed but it
would not be executed unless we ignite it. How do we ignite it? By simply adding
parentheses to the name of the function in our code.
fn(); // [Console] "I am a sweet simple function"
fn
is the name of the function. fn()
is its execution.
Functions as arguments
Let us consider the code snippet below
function acceptArgumentAndLogIt(arg) {
console.log(arg);
}
acceptArgumentAndLogIt("an argument"); // [Console] "an argument"
acceptArgumentAndLogIt
simply accepts an argument under the identifier arg
and logs it. In the last line of the snippet, “an argument” is passed to
acceptArgumentAndLogIt
and arg
then represents “an argument”. What happens
if instead of accepting an argument and logging it, we accept a function as an
argument, and run it. Let’s see!
function acceptFunctionAndRunIt(fn) {
fn();
}
function logSomething() {
console.log("something");
}
acceptFunctionAndRunIt(logSomething); // [Console] "something"
We defined two functions. acceptFunctionAndRunIt
accepts a function and runs
it (as in our fuel analogy, it ignites it by appending parentheses to it). The
other logSomething
holds the ability to log “something” to the console.
In the last line of the snippet, we call acceptFunctionAndRunIt
and pass in
logSomething
to it. logSomething
will to be represented as fn
within it
and because of this, fn
will be a reference to logSomething
. When
acceptFunctionAndRunIt
runs, it sees fn
and asks what does fn
refer to?
The JavaScript runtime rightly tells it that it refers to the the function
assigned to the label logSomething
. That function is retrieved and executed,
and then “something” appears on the console.
So what is a callback function?
Any function A, that is passed into another function B to be executed within function B is a callback function. The reason why it is called by that name is because function A is usually passed into function B so that function B can call it after running some operations. It will be easier to grasp if instead of saying “callback”, one says “call later”, as in the phone conversation analogy.
Imagine that you have a phone conversation with a friend, and you need information from them, but they don’t have it at hand, so you say ‘call me back when you have it’. Your friend goes on to get the information and then calls you or ignites you afterwards. Let’s make a code snippet out of this.
function useInfo(info) {
console.log(info);
}
function getInfo(giveInfoToFriend) {
// getInfoFromBagOfSecrets() returns ["secret one","secret two"],
// takes about 2 mins, hypothetically
const info = getInfoFromBagOfSecrets();
giveInfoToFriend(info);
}
getInfo(useInfo);
When the JavaScript runtime runs, it stores the two functions defined under
their respective identifiers. In the last line of the snippet, we call getInfo
and pass in useInfo
as an argument. When getInfo
runs, it accepts the
reference to the function assigned to useInfo
as giveInfoToFriend
, thus
within getInfo
, useInfo
is the same as giveInfoToFriend
. Information is
gotten from the bag of secrets and is passed to giveInfoToFriend
.
In order to execute useInfo
, we add the parentheses and pass info
to it. The
execution causes what info
represents to be logged to the console. The
function passed in as giveInfoToFriend
is a callback function because it was
passed in to be called at a later time, that is, when the information has been
retrieved.
Real life examples
Callback functions exist in many places and you’ve seen them, you probably didn’t see them that way.
window.addEventListener("eventname", callbackFn)
Remember, a callback function is one that is passed into another function to be
called at a later time. If we have
window.addEventListener("click", callbackFn)
in our code, we do not know
when the user will click on the window, so we say whenever the user clicks,
pass in the necessary arguments to callbackFn
and run it. callbackFn
will
be executed at a later time, specifically when the user clicks on the window.
fs.read(fd, [options,] callbackFn)
In Node.js, we have the ability to read files present on the computer. The
fs.read
function is an asynchronous one, in otherwords, it may or may not be
completely run before the code that is written after it. Since we do not know
when it will be completely run, we pass in callbackFn
when we execute it and
say look, we don’t know when you’ll be done running, but whenever you’re done,
whatever result that you get, pass it into callbackFn
and then run
callbackFn
.
Image credits