Skip to main content

Command Palette

Search for a command to run...

Debouncing in Javascript.

Updated
View as Markdown
Debouncing in Javascript.

While designing web applications, we need to optimize for certain scenarios, where we don't want to call a function repeatedly multiple times.

An example of this could be, a callback function that is executed on window resize or a scroll event. And let's say on top of this, the callback was also making an XHR request. A scenario like this could temporarily freeze up the browser and thus deteriorating the user experience.

A practical example of this is the search bar in most of the popular eCommerce web applications. Here we don't want to make a network request on every keystroke the user is typing to fetch the results. We want the user to finish typing and then wait for a specified window of time to see if the user is not going to type anything else or has finished typing and return the result.

In the below screenshot, you can see, even though I've typed 5 words, 2 new network calls being made. (The first one shown below was made on onfocus event). image.png

To solve such a problem for optimizing the web app, we've got a technique called Debouncing.

1. What is Debouncing?

  • Debouncing is a term taken from Electronics and one of the first articles' about it's use in Web App Development was by John Hann in his blog Debouncing Javascript Methods. So according to him, 'Debouncing ensures that exactly one signal is sent for an event that may be happening several times — or even several hundreds of times over an extended period. As long as the events are occurring fast enough to happen at least once, in every detection period, the signal will not be sent!'
  • So in terms of programming, Debouncing is an optimization technique that ensures, a function is called only once, no matter how many times the event (which is supposed to call the function) is triggered within a specified time frame.

2. Debouncing Implementation

In the following example, on an input tag's keypress event, we've attached an event listener which will make a network call request. If we simply make the network call on every keypress event, it'll cause performance issues on our website. So in order to reduce the total number of network/function calls being made, we'll use the debouncing method shown below.

const input = document.querySelector("input");
input.addEventListener("keypress", debounce(getData, 300));

function getData(){
  // some time consuming network call
}

function debounce(fn, delay){
  let timer = null;
  return function debounced(...args){
    const context = this;
    clearTimeout(timer);
    timer = setTimeout(()=>{
      fn.apply(context, args);
    }, delay)
  }
}

So what did we do above?

  • In simple terms, we've written the logic that will ensure, that our function getData is only called if the user doesn't press any key within the 300 milliseconds timeframe.
  • To break it further down, in order to fetch the data, getData has to be called, but also we don't want to call this method on each keypress event. So we passed this function to the debounce function and got the debounced version of this function back, which is then attached to the event listener.
  • Technically we're attaching this debounced function to the event listener.

So why do we need the debounce function then?

  • We need this function to create a closure and have the value of the timer variable persist between multiple function calls.

Why do we need a closure with timer variable?

  • So suppose in the input textbox, I'm typing something.
  • On every keypress, this debounced function will be called.
  • For Example, If I entered 'abcde', the debounced function will be called 5 times. First time, when we press 'a', a timer will start for 300ms. That timer id will be stored in timer variable. Next time when I press 'b', again the debounced function will be called, but since we had formed a closure with timer variable, we'll be able to access the previous timer value created in case of 'a'. So we'll first clear that out and then start a new timer. So similarly we'll keep creating new timers and clearing them out, until we reach the last function call. After that since no key is pressed, this timer will complete and finally run our getData function.
  • So to summarize, the debounced function will be called as many times the event is triggered, but the getData function will only be called once the user stops typing and there's a delay of 300ms.

What is the debounce function?

  • It's a function that ensures that the function passed into it fn, is called only once within the given time frame delay.

So How Debouncing is working? (Summary)

  • It works by issuing a setTimeout at the specified detection period. Each time the function is called the setTimeout is cancelled and issued again. This serves as our detection mechanism but using reverse logic. It/when the setTimeout executes, we know that our funciton was not called within the detection period.

3. Examples

  • Scrolling a page.
  • Resizing a page.
  • An input search box to fetch suggestions.

4. Debounce implementations in libraries

LibraryExample
Lodash_.debounce(getData, 300);
Underscore_.debounce(getData, 300);

5. See Also

More from this blog

Shivaansh Agarwal's Blog

16 posts

I'm a Software Developer with 2 years of experience specializing in Web Development, who loves to code and build projects. Currently working with ReactJS, JS, Typescript, TailwindCSS.