Implement debounce using vanilla javascript

Let's talk about another common frontend interview question which is implementing debouncing. This is a simple question most big companies ask and even I have asked this question many times.

Before we proceed with the implementation, let's first understand debouncing.

What's debouncing in javascript?

Debouncing is a technique in JavaScript that limits the rate at which a function can fire. It is used to prevent a function from being called too frequently, particularly in situations where a user may trigger the function many times in a short period. Debouncing is achieved by adding a small delay before executing a function, so if the function is triggered multiple times within the delay, it will only be executed once, at the end of the delay.

Let's take scrolling for example, we sometimes would need to scroll events and perform some operations based on it. The resulting code would look something like this:

window.addEventListener('scroll', () => {
  // This code will execute on every scroll event
  console.log('Scrolled!');
});

This would print Scrolled! in the console for every pixel that the user scrolls. This would result in unnecessary wastage of operations and calculations that we do as a part of the callback. This can be a problem if the code inside the event listener is computationally expensive or if it causes a layout reflow or repaint.

Ideally, we would only want the operations and calculations to happen when the user stops/pauses scrolling. Something like this:

window.addEventListener('scroll', () => {
  // Expectation: Modify the implementation so that This code executes only when the user pauses or stops scrolling
  console.log('Scrolled!');
});

Let's go ahead and implement the debounce function using vanilla javascript. So, the structure of the debounce method would look something like this:

function debounce(func, delay) {
    ...
}

It takes a func (a function that needs to be invoked) and a delay (delay probably in ms to wait until we invoke the function) and that would return a function.

Implementation

function debounce(func, delay) {
  let timeoutId;
  return function() {
    const context = this;
    const args = arguments;
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => func.apply(context, args), delay);
  }
}

This is a simple implementation of debounce using vanilla javascript. It returns a function. Inside the new function, a timeoutId variable is declared and set to undefined. When the new function is called, it first captures the this context and the arguments passed to it, which are stored in the context and args variables, respectively.

The clearTimeout function is called with the timeoutId to clear any existing timeout. This is done to ensure that the debounced function is only called once after the specified delay has elapsed since the last time the original function was called.

Then, a new timeout is set using the setTimeout function, which calls the original function func after the specified delay has elapsed. The apply method is used to call the original function with the captured context and args variables, which ensures that the original function is called with the correct this context and arguments.

Now, let's add this to our previous scroll event and notice the improvement.

function handleScroll() {
  // This code will execute once, after the user has finished scrolling
  console.log('Scrolled!');
}

const debouncedScroll = debounce(handleScroll, 100);
window.addEventListener('scroll', debouncedScroll);

Here, we are passing the debounced method instead of the direct method. With this, as the user scrolls there won't be any event fired and only when the user pauses or stops, there will be an event and the necessary computations associated with that can happen. This improves performance and memory usage by a large fold. I would highly recommend developers consider this option when working with frequent events/updates.

Common use cases of Deboucing in web

This debounce is a general technique and is definitely not limited to only scroll events. You could even use this in the following use cases as well:

  1. Auto suggestions/Auto completions in Search: Rather than firing the user-typed query over the call for every character typed, you could instead wait until the user pauses/stops and get those results as they are more relevant and appropriate anyway. The following code shows how this can be used in the search as well.

     function search() {
       // Your search function code here
     }
    
     const debouncedSearch = debounce(search, 500); // Debounce search function with 500ms delay
    
     document.getElementById('search-box').addEventListener('keyup', debouncedSearch);
    
  2. Infinite scroll: Debouncing can be used to limit the number of API requests made when implementing infinite scroll functionality. This ensures that only a reasonable amount of data is loaded at any given time, preventing unnecessary server requests and reducing the overall load time of the page.

  3. Resize events: Debouncing can be used to prevent too many function calls during window resize events. This is important because too many function calls can lead to performance issues and slow down the web page.

  4. Form submissions: Debouncing can be used to prevent multiple form submissions when a user clicks the submit button multiple times. This prevents the server from being overwhelmed with duplicate requests and ensures that only one request is processed.

  5. Delayed loading of content: Debouncing can be used to delay the loading of content until a certain time period has elapsed. This is useful when loading large or complex data sets, such as images or videos, and helps improve the overall performance of the web page.

  6. Mouse movement events: Debouncing can be used to limit the number of requests sent to the server when a user moves their mouse over certain elements on the page. This prevents the server from being overwhelmed with requests and ensures a smoother user experience.

  7. ...

Benefits of debouncing

Debouncing has several benefits, including:

  1. Improved performance

  2. Reduced server load, and

  3. A smoother user experience.

By limiting the rate at which a function is executed, debouncing reduces the amount of processing required, which can help improve performance and reduce server load. Debouncing can also help create a smoother user experience by preventing jarring transitions or animations that can occur if a function is executed too frequently.

Debouncing is a powerful technique in JavaScript. With the help of the setTimeout method and a little bit of code, you can easily implement debouncing in your JavaScript projects.

Click here if you want to read about throttling in javascript.

I hope this article was helpful to you. If yes, please make sure to read my other articles. I am currently writing a series on common "Frontend engineering interview questions" based on my experience interviewing people at Amazon, RedHat, VMware, and Snapdeal, ...

Please subscribe to the newsletter to get the articles delivered directly to your mailbox.

#2Articles1Week

Cheers

Arunkumar Sri Sailapathi.