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 pattern 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.
Let's see a more obvious example and compare it to what we would expect without debouncing.
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.
Write a function
debounce that takes a callback function and a
wait time and prevents the callback from executing until the
wait time elapses.
Anytime the debounce function is called, it should restart the timer and only execute the callback once the
wait completely elapses.
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
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
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.
timeout: The value used to indicate a running debounce.
We can use a debounce by doing:
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 spreads over the parameters (
...args) to allow for the debounce function to receive any number of arguments to pass to the callback.
We declare a callback function named
later which is the function that is executed after the end of the debounce timer.
This is what will be called after the
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.
This in turn calls
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,
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.
Common scenarios for a debounce are resize, scroll, and keyup/keydown events. In addition, you should consider wrapping any interaction that triggers excessive calculations or API calls with a debounce.
Here’s a commented version of the function as well.
Table of Contents