DEV Community 👩‍💻👨‍💻

Malcolm Kee
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);
  }
}
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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');
  }
);
Enter fullscreen mode Exit fullscreen mode

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() {
        alert('FizzBuzz');
      },
      function() {
        alert('Fizz');
      },
      function() {
        alert('Buzz');
      }
    );
    
  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);
  }
);
Enter fullscreen mode Exit fullscreen mode

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) {
        alert(msg);
      }
    );
    
  2. append the message into a div with id message

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

Top comments (0)

This post blew up on DEV in 2020:

js visualized

🚀⚙️ JavaScript Visualized: the JavaScript Engine

As JavaScript devs, we usually don't have to deal with compilers ourselves. However, it's definitely good to know the basics of the JavaScript engine and see how it handles our human-friendly JS code, and turns it into something machines understand! 🥳

Happy coding!