Malcolm Kee

Posted on

Understanding Callback function

There are two ways to look at callback function:

1. callback function as a way to reuse code for different operations
2. callback function as a way for async programming

In this article, we'll learn the first way to look at callback function.

Prerequisite: you need to understand `while` loop to understand the example

Parameters to reuse code for different data

Let's recap the concept of using parameter to allow function to run code for different data.

Consider the following FizzBuzz code:

``````let i = 1;

while (i <= 100) {
if (i % 3 === 0 && i % 5 === 0) {
// log 'FizzBuzz' when the result is multiply of 3 and multiply of 5
console.log('FizzBuzz');
} else if (i % 3 === 0) {
// log 'Fiz' when the result is multiply of 3
console.log('Fizz');
} else if (i % 5 === 0) {
// log 'Buzz' when the result is multiply of 5
console.log('Buzz');
} else {
// log the number
console.log(i);
}
}
``````

Assuming that we need to support additional two scenarios:

1. Do the FizzBuzz for numbers from -50 to 50
2. Do the FizzBuzz for numbers from 0 to 1000

To do so, instead of copy pasting our FizzBuzz code above, let's create a function:

``````function fizzBuzz() {
let i = 1;

while (i <= 100) {
if (i % 3 === 0 && i % 5 === 0) {
// log 'FizzBuzz' when the result is multiply of 3 and multiply of 5
console.log('FizzBuzz');
} else if (i % 3 === 0) {
// log 'Fiz' when the result is multiply of 3
console.log('Fizz');
} else if (i % 5 === 0) {
// log 'Buzz' when the result is multiply of 5
console.log('Buzz');
} else {
// log the number
console.log(i);
}
}
}

fizzBuzz();
``````

The code will still works as before. However, now that we put the code into a function, we can "parameterize" the data (start number (`1`) and end number (`100`)) so that we can support different start/end numbers:

``````function fizzBuzz(start, end) {
let i = start;

while (i <= end) {
if (i % 3 === 0 && i % 5 === 0) {
// log 'FizzBuzz' when the result is multiply of 3 and multiply of 5
console.log('FizzBuzz');
} else if (i % 3 === 0) {
// log 'Fiz' when the result is multiply of 3
console.log('Fizz');
} else if (i % 5 === 0) {
// log 'Buzz' when the result is multiply of 5
console.log('Buzz');
} else {
// log the number
console.log(i);
}
}
}

fizzBuzz(1, 100);
``````

Now that `fizzBuzz` is a function that can be used to support the additional two scenarios:

1. Do the FizzBuzz for numbers from -50 to 50

``````fizzBuzz(-50, 50);
``````
2. Do the FizzBuzz for numbers from 0 to 1000

``````fizzBuzz(0, 1000);
``````

Parameters to reuse code for different operations

Let's take one step further, how can we abstract away the `console.log` calls?

Assuming that we need to run similar logic like FizzBuzz, but this time, we need to show the FizzBuzz/Fiz/Buzz messages in two other ways:

1. `alert` the message
2. append the message into a `div` with id `message`

What we'll do, is similar to what we did with "parameterize" data in previous section, it is just that we will "parameterize" the statement instead.

Let's see:

``````function fizzBuzz(start, end, showFizzBuzz, showFizz, showBuzz) {
let i = start;

while (i <= end) {
if (i % 3 === 0 && i % 5 === 0) {
// log 'FizzBuzz' when the result is multiply of 3 and multiply of 5
// console.log('FizzBuzz');
showFizzBuzz();
} else if (i % 3 === 0) {
// log 'Fiz' when the result is multiply of 3
// console.log('Fizz');
showFizz();
} else if (i % 5 === 0) {
// log 'Buzz' when the result is multiply of 5
// console.log('Buzz');
showBuzz();
} else {
// log the number
console.log(i);
}
}
}

fizzBuzz(
1,
100,
function() {
console.log('FizzBuzz');
},
function() {
console.log('Fizz');
},
function() {
console.log('Buzz');
}
);
``````

What we did:

1. Comment out the previous `console.log` and replace it with calling the 3 new parameters `showFizzBuzz`, `showFizz`, and `showBuzz`.
2. pass additional 3 arguments (each of them a function) for the 3 new parameters we introduce.

The reason that we can "parameterize" statement is that we can pass function as parameter in JavaScript.

Now we can support the additional two scenarios:

1. `alert` the message

``````fizzBuzz(
1,
100,
function() {
},
function() {
},
function() {
}
);
``````
2. append the message into a `div` with id `message`

``````fizzBuzz(
1,
100,
function() {
const target = document.querySelector('#message');
target.append('FizzBuzz');
},
function() {
const target = document.querySelector('#message');
target.append('Fizz');
},
function() {
const target = document.querySelector('#message');
target.append('Buzz');
}
);
``````

Passing Parameter to Callback Function

With the current code, you may realize that we need to pass 3 functions to `fizzBuzz`, and they are almost similar except the message is different.

We can simplify it by realizing the fact that callback function can accept parameters. Let's see how to simplify the `fizzBuzz` code:

``````function fizzBuzz(start, end, showMessage) {
let i = start;

while (i <= end) {
if (i % 3 === 0 && i % 5 === 0) {
// log 'FizzBuzz' when the result is multiply of 3 and multiply of 5
// console.log('FizzBuzz');
// showFizzBuzz();
showMessage('FizzBuzz');
} else if (i % 3 === 0) {
// log 'Fiz' when the result is multiply of 3
// console.log('Fizz');
// showFizz();
showMessage('Fizz');
} else if (i % 5 === 0) {
// log 'Buzz' when the result is multiply of 5
// console.log('Buzz');
// showBuzz();
showMessage('Buzz');
} else {
// log the number
console.log(i);
}
}
}

fizzBuzz(
1,
100,
function(msg) {
console.log(msg);
}
);
``````

What we did:

1. Replace the 3 parameters `showFizzBuzz`, `showFizz`, and `showBuzz` with a single generic `showMessage` parameter.
2. Call `showMessage` with the message as parameter.
3. replace the previous 3 function arguments with a single function that will accept `msg` as parameter.

The updated 2 more scenarios:

1. `alert` the message

``````fizzBuzz(
1,
100,
function(msg) {
}
);
``````
2. append the message into a `div` with id `message`

``````fizzBuzz(
1,
100,
function(msg) {
const target = document.querySelector('#message');
target.append(msg);
}
);
``````