Debouncing in JavaScript

Photo by Erik Mclean on Unsplash

Debouncing in JavaScript

Hello and welcome to my blog! Today, we'll be learning about debouncing in JavaScript. This is a widely used concept in a lot of applications and I hope you learn why and how to implement this in JavaScript.

Deboucing

Let's start with a formal definition of debouncing.

Debounce functions are higher-order functions using which we can control the rate at which another function is called.

Let's break down this definition and analyze its parts.

First, what is a higher-order function in JavaScript?

Functions in JavaScript are treated in a wildly different manner than in other languages. They can be declared in the usual manner, but they can also be assigned to a variable, declared anonymously, or invoked immediately. You can go through this article to learn more about functions in JavaScript.

A higher-order function in JavaScript is nothing but a regular function that either:

  1. returns a function.

  2. takes a function as one of its parameters.

A debounce function will satisfy both of these conditions. Let's first look at an example use case for a debounce function.

Consider the search box in Google. As you type your query into the input box, suggestions start popping up. How do you think this works?

Not going into too many details, but there must be some API that is called when you're typing into the search box, which returns these suggestions that are displayed. But, think about how many times this API should be called. Should it be called after every letter that you type? If you have a long query, let's say 100 letters, should this API be called 100 times? That's not very efficient, is it?

A better option would be to call this API after a certain period has elapsed since you stopped typing. This would ensure that there are no unnecessary API calls which will improve the speed and efficiency of the website and save the bandwidth of the user's internet.

How to implement Debouncing?

Let's see a sample implementation of debouncing in JavaScript. A debounce function is a mere seven lines long. That's it.

function debounce(callback, delay) {
    let timeout;
    return function() {
        clearTimeout(timeout);
        timeout = setTimeout(callback, delay);
    }
}

Let's go over this function line by line. We pass two parameters to this function:

  1. callback: the function that needs to be debounced.

  2. delay: the amount of time(in milliseconds) that needs to pass before callback can execute again.

We declare a variable timeout that will hold the timeoutID returned by setTimeout. Then, we return an anonymous function that closes over the timeout variable so that we can retain access to it even after the initial call to debounce has finished executing.

This concept in JavaScript is called closure.

In JavaScript, a closure is a function that has access to variables defined in an outer function that has already returned. A closure is created when a function is defined inside another function and then returned from that outer function. The inner function can access the variables and parameters of the outer function, even after the outer function has completed execution.

Closures are one of the more perplexing concepts in JavaScript and discussing it in detail would be out of the scope of this article. If you want to dive into this topic I'd recommend reading the MDN docs or Chapter 7 of "You Don't Know JS".

Inside the anonymous function that we return, we call the clearTimeout method. This makes sure that each time our debounce function is called, the timeout variable is reset and the counter can start over.

At last, we reach the line where we call our debounced function but, inside a setTimeout. This will create a timeout that executes our passed callback once delay amount of time has passed.

The callback function will only execute if the timeout reaches 0. But, since we are resetting the timeout each time our debounce function is called, the execution rate of our function is limited.

Actual Implementation

Let's look at the example of debouncing for a search field. First, we use a simple HTML input field as our search box.

<label for="search">Search for something</label>

<input id="search" type="text">

Next, we create a function that is called every time something is typed into the search box. Assume this is an API call that returns a list of suggestions.

function mockAPI(){
    console.log("this is a mock api call");
}

Finally, we bind this function to our input field.

const searchBox = document.getElementById("search");
searchBox.addEventListener(
    "keyup",
    debounce(mockAPI,2000)
);

That's it! Now, every time we type something into the search box, mockAPI will execute if at least 2 seconds have passed since the last time we typed something in.

Conclusion

That's all I have for you on debouncing. I hope you learned something new and I hope this concept wasn't too hard to grasp!

Consider subscribing to my newsletter for more such content delivered right to your inbox!

Cheers!