In JavaScript, whenever we’re attaching a performant-heavy function to an event listener, it's considered best practice to control how often the function is called. In this tutorial, we’ll take a look at how to implement debounce and throttle functions for regulating events.
It’s All About Performance
Performance is a major concern when building web pages, especially for sites which carry out animations and interactions. Event listeners are a common choice for implementing interactions with JavaScript as they are used to detect changes on a page and call functions according to those changes. It's important to ensure that event listener scripts are optimised for performance.
How do Event Listeners Impact Performance?
Let’s look at how often event listeners are called according to user actions. Carry out the corresponding events in the demo below to see the count:
Event listeners impact performance depending to the events they’re calling.
Let’s assume we have a function that’s responsible for adding new elements to the DOM and we call this function every time the user scrolls. As we’ve seen in the demo, the scroll event listener can be called for every pixel the user scrolls the screen.
Adding elements to the DOM cause reflows, which is the browser’s way of calculating the placement for new elements. Reflows are done in a cascading manner so changing the reflow of one element will cause a change in all subsequent elements and part or all of the document to be re-rendered. These calculations can affect user speed and slow down your page. You can read more on how reflows and repaints affect performance in this article.
Whenever we’re attaching a performant-heavy function to an event listener, it’s considered best practice to control how often the function is called.
Debounce and Throttle are two methods for optimising performance of scripts by controlling how often an event is called.
Debounce vs. Throttle
The major difference between debouncing and throttling is that debounce calls a function when a user hasn’t carried out an event in a specific amount of time, while throttle calls a function at intervals of a specified amount of time while the user is carrying out an event.
For example, if we debounce a scroll function with a timer of 250ms (milliseconds), the function is only called if the user hasn’t scrolled in 250ms. If we throttle a scroll function with a timer of 250ms, the function is called every 250ms while the user is scrolling.
This demo provides a visual demonstration of the example above:
Debounce Function With Vanilla JavaScript
Let’s expand more on how the concept of debouncing works.
We can think of it as a wait-and-see function. When we debounce a function, we wait to see if the user carries out an action in a specified amount of time and if not, we’ll run the function. Now let’s convert that to logic.
- Initialise a
timer
variable that controls when to run a callback function - Reset the timer function every time the user starts an action
Here’s what the logic implementation looks like:
let debounceTimer; const debounce = (callback, time) => { window.clearTimeout(debounceTimer); debounceTimer = window.setTimeout(callback, time); }
In this code, we’ve initialised a timer variable called debounceTimer
that controls a setTimeout method. This setTimeout method is what’s responsible for calling the function callback
after the specified parameter time
.
Inside our debounce function, we first clear the debounceTimer
function every time the debounce function is called. This is what ensures that the callback function isn’t called until the time has been exhausted. If we set out debounce function inside a scroll event listener, the debounceTimer
is reset by window.clearTimeout
every time the user scrolls.
Use Cases for Debounce
Debouncing is a good method for controlling events that require sporadic user actions such as typing in an input field or clicking a button. In the case of a search bar that makes API calls according to user input, implementing a debounce is a good way to reduce the number of calls made to the API.
In this demo, we’ve debounced the user input to only return the input value if the user hasn’t typed anything in 500ms:
We can write the code for the above demo like this:
let input = document.getElementById('name'); let debounceValue = document.getElementById('debounce-value'); const updateDebounceValue = () => { debounceValue.innerHTML = input.value; } let debounceTimer; const debounce = (callback, time) => { window.clearTimeout(debounceTimer); debounceTimer = window.setTimeout(callback, time); }; input.addEventListener( "input", () => { debounce(updateDebounceValue, 500) }, false );
Implement a Throttle Function With Vanilla JavaScript
We can think of throttle as a minimizing function; it minimizes the number of calls made within a certain time interval.
Defining the logic for a throttle, we have:
- Initialize a variable to detect if the function has been called within the specified time
- If the function has been called, pause the throttle function
- If the function hasn’t been called or is done running in the interval, rerun the throttle function
We can write this in JavaScript as:
//initialize throttlePause variable outside throttle function let throttlePause; const throttle = (callback, time) => { //don't run the function if throttlePause is true if (throttlePause) return; //set throttlePause to true after the if condition. This allows the function to be run once throttlePause = true; //setTimeout runs the callback within the specified time setTimeout(() => { callback(); //throttlePause is set to false once the function has been called, allowing the throttle function to loop throttlePause = false; }, time); };
Here’s a breakdown of what’s happening:
throttlePause
is initially undefined, so the function moves on to the next line.- We set
throttlePause
totrue
for the next iteration. If the throttle function is called again beforesetTimer
is done, the function returns undefined. setTimeout
starts a timer to run the function. The timer runs asynchronously while the throttle function is being called so it won’t be affected by the throttle function being called again.- Once the callback has been run, it sets
throttlePause
tofalse
so the throttle function can loop.
Use Cases for Throttle
Throttle is useful for cases where the user is carrying out a smooth or continuous event such as scrolling or resizing. In the event of animating elements based on their scroll position or handling an infinite scroll page, we can use throttle to control how often the scroll handler is called.
In this demo, we’ve set a throttle function on a scroll event listener:
The code for the above demo looks like this:
let throttleTimer; const throttle = (callback, time) => { if (throttleTimer) return; throttleTimer = true; setTimeout(() => { callback(); throttleTimer = false; }, time); } window.addEventListener("scroll", () => { throttle(handleScrollAnimation, 250); });
Conclusion
Now you should have a better understanding of the concepts and differences between throttle and debounce. We’ve also explored the logical approaches and code implementation of both methods so you can apply them to real life situations in order to optimize the performance of your scripts.
Learn More About Front-end JavaScript
From beginner concepts, to practical projects, we have you covered:
No comments:
Post a Comment