Throttling, Debouncing and RequestAnimationFrame in JavaScript
Mitigating the resource-intensive tasks in an application
Introduction
In web development, resource-intensive tasks such as event handling, DOM manipulation, and API requests can sometimes lead to performance issues and a sluggish user experience. To mitigate these problems, developers can employ techniques like throttling, debouncing, and utilizing the RequestAnimationFrame API. In this blog post, we will explore these powerful strategies that can help optimize the execution of resource-intensive tasks, resulting in smoother and more responsive web applications.
Understanding Resource-Intensive Tasks
Resource-intensive tasks are operations that require significant computational resources, such as handling frequent events, making multiple API requests, or performing extensive DOM manipulations. Scenarios like scrolling, window resizing, or keystroke events emit such tasks excessively and can impact the performance and responsiveness of web applications, leading to laggy interfaces and increased resource consumption.
Throttling
Throttling is a technique used to limit the frequency at which a particular function or event handler is executed. By setting a maximum execution rate, throttling helps control the number of times a function is called during a specified time interval.
How Throttling Works
Throttling involves setting a timer that delays the execution of a function. If subsequent function calls occur before the timer expires, they are ignored until the timer resets. This prevents the function from executing too frequently and overwhelming system resources.
A sample implementation of throttling
function throttle(foo, limit){
let inThrottle;
return function(){
const context = this;
const args = arguments;
if(!inThrottle){
foo.apply(context, args);
inThrottle = true;
setTimeout(() => (inThrottle = false), limit)
}
}
}
Debouncing
Debouncing is another technique used to manage resource-intensive tasks by ensuring that a function is executed only after a specified quiet period. It helps prevent unnecessary function invocations, particularly during rapid-fire events like scrolling or keyboard input.
How Debouncing Works
Debouncing involves setting a timer that delays the execution of a function. If subsequent calls to the function occur before the timer expires, the timer resets. The function is executed only when the quiet period elapses without any new function calls.
A sample implementation of debouncing
function debounce(foo, delay){
let timeOutFunction;
return function(...args){
clearTimeout(timeOutFunction);
timeOutFunction = setTimeout(() => {
foo.apply(this, args);
}, delay);
}
}
Difference between Throttling and Debouncing
Throttling is concerned with limiting the execution frequency of a function or event handler to a predefined rate while debouncing focuses on delaying the execution until a quiet period occurs, discarding intermediate invocations.
Throttling ensures periodic execution while debouncing guarantees execution after a pause. The choice between throttling and debouncing depends on the specific use case and the desired behavior for managing function execution frequency.
Debounce is most suitable for control events like typing or button clicks. Throttling is most suitable for continuous user events like resizing and scrolling.
Leveraging RequestAnimationFrame (RAF) for Animation and Rendering
Rendering in the browser is done by using another queue called the Render Queue. The browser runs at 60 fps (renders every 16.67 ms). Similar to Task Queues the render won't happen when the Callstack is not empty. To render something, some task needs to be executed that makes some changes in the UI (changes in DOM). This task execution can be done in multiple ways.
Suppose you are using setTimeout(..., 0)
for animation purposes and the result might not be smooth as expected. Why? Because setTimeout()
executes the tasks more frequently than it renders (setTimeout(..., 0)
is not 0 it is 4 ms as discussed in the last blog, so in 16.67 ms roughly four tasks will be executed). Suppose we use 16.67 ms and simulate smooth animation will be it efficient? No, because there might be some inaccuracies in setTimeout()
such that some tasks might shift their position to the next render step.
The solution for this problem is the RequestAnimationFrame (RAF), it is an API provided by modern browsers that allow developers to synchronize tasks with the browser's rendering process. It makes sure that the task is executed before rendering. It is commonly used for smooth animations and efficient rendering.
Given below are some of the reasons why we use requestAnimationFrame,
RAF automatically adjusts the frame rate based on the user's device capabilities and the browser's rendering workload.
By synchronizing animations with the browser's rendering cycle, unnecessary computations and rendering operations are avoided during idle periods.
// Using setTimeout, Not so smooth and fast animation
document.querySelector(".button").addEventListener("click", () => {
let position = 0;
function animate() {
document.querySelector(".animatingbox").style.left = ++position + "px";
setTimeout(animate, 0);
}
setTimeout(animate, 0);
});
// Using RequestAnimationFrame, Smooth and animates according
// to the browser's render cycle
document.querySelector(".button").addEventListener("click", () => {
let position = 0;
function animate() {
document.querySelector(".animatingbox").style.left = ++position + "px";
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
});
Conclusion
Resource-intensive tasks can negatively impact the performance of web applications. By implementing throttling, debouncing, and leveraging the power of RequestAnimationFrame, developers can effectively manage and optimize these tasks, resulting in smoother interactions, improved responsiveness, and a better overall user experience. Understanding these techniques and applying them judiciously will empower developers to create performant web applications that delight users.
Remember to profile and test your implementations to fine-tune performance and ensure your chosen strategies align with the specific needs of your application. With these techniques in your toolbox, you are equipped to mitigate resource-intensive tasks and unlock the full potential of your web applications. Happy optimizing!