Throttle
A throttle is a common technique used in the browser to improve an application's performance by limiting the number of events your code needs to handle. You should use a throttle when you want to execute a callback at a controlled rate, allowing you to handle intermediate states repeatedly at each fixed interval of time.
On the left, side we have normal events that aren't throttled and the event handler is executed each moment the page is scrolled. On the right, we have a throttled event handler and will only execute the function once every 1 second.
Implementing a throttle 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.
A throttle is a cousin of the debounce, 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 throttle
that takes a callback function and a delay
time and limits the callback from executing to once for each window of delay
milliseconds.
If the function is executed multiple times, throttle
should handle it every delay
milliseconds in evenly spaced increments.
You only care about the most recent event and should ignore any previous ones if a new value comes in.
Throttling in the Context of Web Development
Imagine you have a scroll event handler where you want to display new content to a user as they move down the page. If we executed the callback every time the user scrolls a single pixel, we would quickly get clogged with events if they rapidly scroll because it would send hundreds or thousands of events in quick succession. Instead, we throttle it so that we only check the distance scrolled every 100ms so that we're getting only 10 callbacks per second. The response will still feel instantaneous to the user but be much more efficient computationally.
A throttle creates evenly-spaced function calls. Imagine if you were performing some heavy calculation or an API request in the event handler callback function. By throttling these callbacks, you prevent the app from freezing or unnecessarily hammering your server with requests.
Throttle Implementation in JavaScript
Let's immediately jump into the code for a throttle. I'll describe it below and then also provide a commented version of the function.
A throttle is a higher-order function, which is a function that returns another function (named throttledEventHandler
here for clarity).
This is done to form a closure around the callback
, delay
, throttleTimeout
, and storedEvent
function parameters.
This preserves the values of each variable to be read when throttledEventHandler
is executed.
The following is a definition of each variable:
callback
: The throttled function that you want to execute at a given rate.delay
: The amount of time you want the throttle function to wait between executions of thecallback
.throttleTimeout
: The value used to indicate a running throttle created by oursetTimeout
.storedEvent
: The event that you want to handle with the throttledcallback
. This value will continually be updated until the throttle ends. Then it will executecallback
with the most recent value.
We can use our throttle as in the following code:
Since a throttle returns a function, the throttledEventHandler
from the first example and the returnedFunction
function from the second example are actually the same function. Every time a user scrolls, it will execute throttledEventHandler
/returnedFunction
.
Let's step through what happens when we throttle a function. First we create a closure around variables so they will be available to throttledEventHandler
on every execution. throttledEventHandler
receives 1 argument which is the event. It stores this event in the storedEvent
variable.
Then we check if we have a timeout running (ie. an active throttle). If we do have a throttle, then throttledEventHandler
has completed this execution and waits to execute the callback. If the throttle is not active, we are able to immediately handle the event with our callback function. We then call setTimeout
and store the timeout value which indicates our throttle is running.
While the timeout is active, the most recent event is always stored. The callback execution is bypassed which saves us from performing CPU intensive tasks or calling our API.
When the setTimeout
finishes, we null out the throttleTimeout
indicating that the function is no longer throttled and can handle events. If there is a storedEvent
, we want to handle it immediately, and to do this we call throttledEventHandler
recursively. The recursive call inside the setTimeout
is what allows us to handle events at a constant rate. It will repeatedly execute the same process after a desired delay as long as new events continue to come in.
See a throttle in action and play around with it in this Codepen.
A commented version of the function as well: