Debounce in JavaScript — Improve Your Application’s Performance

An explanation of a debounce function and how to use it in your JavaScript code to improve performance

Have you noticed that when you type into a search input, there will be a delay before the typeahead results appear. This functionality is frequently controlled by a function called a debounce (it could also be a throttle function that has a similar outcome). The debounce function delays the processing of the keyup event until the user has stopped typing for a predetermined amount of time.

This prevents your UI code from needing to process every event and also drastically reduces the number of API calls sent to your server. Processing every character as it’s entered could harm performance and add unnecessary load to your backend.

Implementing a debounce from scratch is a common interview question. It tests your understanding of intermediate and advanced JavaScript concepts such as: async programming, callbacks, scope, and closures. It is also a practical solution used in real-world applications to improve performance and demonstrates that you understand the tools to write good code for real users.

I built a course to help you master the coding interview >

A debounce is a cousin of the throttle, and they both improve the performance of web applications. However, they are used in different cases. A debounce is utilized when you only care about the final state. For example, waiting until a user stops typing to fetch typeahead search results. A throttle is best used when you want to handle all intermediate states but at a controlled rate. For example, track the screen width as a user resizes the window and rearrange page content while it changes instead of waiting until the user has finished.

Let’s dive in and see what a debounce looks like:

A debounce is a higher-order function, which is a function that returns another function (named executedFunction here for clarity). This is done to form a closure around the func and wait function parameters and the timeout variable so that their values are preserved. The following is a definition of each variable:

func : The function that you want to execute after the debounce time

: The function that you want to execute after the debounce time wait : The amount of time you want the debounce function to wait after the last received action before executing func . For our typeahead example, it would be the amount of time to wait after the last key press.

: The amount of time you want the debounce function to wait after the last received action before executing . For our typeahead example, it would be the amount of time to wait after the last key press. timeout : The value used to indicate a running debounce.

We can use a debounce doing:

var returnedFunction = debounce(function() {

// All the taxing stuff you do

}, 250);



window.addEventListener('resize', returnedFunction);

Since debounce returns a function, the executedFunction from the first example and the returnedFunction function from the second example are the same function. Every time the window is resized, it will execute executedFunction / returnedFunction .

Our executedFunction spreads over the parameters (...args) to allow for the debounce function to receive any number of parameters to pass to the callback.

We declare a callback function named later which is the function that’s executed after the end of the debounce timer. This is what will be called after the setTimeout expires.

Next, we clearTimeout which had prevented the callback from being executed and thus restarts the debounce. Then we (re-)declare timeout which starts the debounce waiting period. If the full wait time elapses before another event, then we execute the later callback function. The timeout is set to null which means the debounce has ended. This executes func(...args) .

There is a more advanced version of this where we can pass an immediate flag to debounce . Currently we always wait until the end of the debounce to execute the callback, but with immediate , you can change it such that the function executes at the leading edge and won’t allow you to execute again until it has delayed calling long enough to deplete the timer.

Here’s a commented version of the function as well.