Optimizing events in JavaScript: debounce and throttle
In modern frontend applications, interface smoothness and browser performance are critical. Many JavaScript functions are triggered by fast, repeated user actions such as scrolling, resizing the window, or typing text. If the number of these calls is not limited, the application can gradually become slower, and interactions may start to feel heavy and unresponsive.
That’s why two popular optimization techniques exist: debounce and throttle. Although they are often mentioned together, they work differently, and choosing the right one depends on the specific use case.
Event optimization
Debounce – waits until the user stops a series of actions
Debounce is a technique that delays function execution until the user stops performing a sequence of repeated actions. Each new event resets the timer. The function is called only after nothing has happened for a specified period of time, for example 200 ms.
function debounce(callback, delay) {
let timeout;
return function executedFunction(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => callback.apply(this, args), delay);
};
}
debounce(myFunction, 200); // 200ms debounce
This approach is ideal when you care about the final result of a user’s action rather than every single interaction along the way. Common examples include:
- search-as-you-type (send the request only after the user stops typing),
- dynamic content filtering,
- layout calculations after window resizing finishes,
- saving settings only after a slider stops moving.
Debounce often reduces the number of function calls to just one, which significantly lightens the load on the application.
Throttle – runs less often, but regularly
Throttle works differently. It allows a function to run no more than once within a specified time interval. It doesn’t wait for the user to finish the action, but it also prevents continuous, uninterrupted execution. This creates a balance: the function runs periodically and consistently, but never too often.
function throttle(callback, limit) {
let waiting = false;
return function executedFunction(...args) {
if (!waiting) {
callback.apply(this, args);
waiting = true;
setTimeout(() => {
waiting = false;
}, limit);
}
};
}
throttle(myFunction, 200); // 200ms throttle
Throttle works especially well in situations where:
- you want to respond to scrolling, but not hundreds of times per second,
- element positions are updated while scrolling,
- scroll-related metrics are being tracked (such as scroll percentage),
- visual effects depend on continuous user movement.
It’s an ideal solution when regular updates are needed without overwhelming the main thread.
In short:
Debounce – wait
Throttle – control the pace
Although debounce and throttle address a similar problem, choosing the right tool has a direct impact on how users experience the application.
Both techniques significantly reduce browser load and help avoid situations where the interface becomes sluggish or stutters during dynamic interactions.