What is debouncing and throttling?
Web pages often listen to frequent events such as resize, scroll and keyup also multiple user initiated events like clicks. Without control, handlers for these events can fire dozens of times per second, causing performance issues or unnecessary network requests. Debouncing and throttling are two patterns for rate-limiting function calls. They serve similar goals but behave differently.
Debouncing
Debouncing delays a function call until a certain amount of time has passed since the last invocation. When an event triggers repeatedly, the debounce wrapper resets its timer each time; only when no new events occur during the wait period does it finally call the function. This technique reduces the number of calls by ensuring the function executes once after a burst of activity【52406376895244†L118-L128】. Common use-cases include auto-saving form data or making API requests after a user stops typing.
Example debounce implementation:
function debounce(fn, delay) {
let timerId;
return (...args) => {
clearTimeout(timerId);
timerId = setTimeout(() => fn.apply(this, args), delay);
};
};
const searchInput = document.getElementById("search");
searchInput.addEventListener("input", debounce(() => {
// This runs only after the user stops typing for 300 ms
performSearch(searchInput.value);
}, 300));
Advantages: Debouncing reduces resource consumption by preventing unnecessary calls.
Disadvantages: It introduces a delay before the action runs; the function won't execute until the
user stops triggering events.
Throttling
Throttling ensures that a function runs at most once per specified interval. When events occur more frequently than the limit, extra calls are ignored or delayed. Unlike debouncing, throttling does not wait for the activity to stop — it guarantees regular execution (e.g., every 100 ms) regardless of how many events fire. Throttling is useful for events like scroll or mousemove where you need periodic updates (such as updating a progress bar or showing scroll position).
A basic throttle implementation can use a flag and timestamps:
function throttle(fn, limit) {
let lastCall = 0;
return (...args) => {
const now = Date.now();
if (now - lastCall >= limit) {
lastCall = now;
fn.apply(this, args);
}
};
};
window.addEventListener("scroll", throttle(() => {
console.log("Scroll position:", window.scrollY);
}, 100));
This version executes the callback immediately and then prevents further calls until the time window
has passed. A more advanced implementation might schedule the final call after the event burst
ends.
Advantages: Throttling enforces a consistent rate of execution. It can keep UI updates smooth
under heavy event load.
Disadvantages: It may skip intermediate events; if the interval is too
long, the handler might miss important changes.
Choosing between them
Both techniques limit how often a function runs:
- Use debounce when you want to wait for a "pause" in activity — for example, delaying an API call until the user stops typing.
- Use throttle when you want periodic updates regardless of continuous activity — for example, updating the scroll position every 100 ms during a scroll.
Real-world analogies
- Debouncing is like waiting until a friend finishes speaking before responding. If they keep talking, you hold off; you only reply once they pause.
- Throttling is like setting a timer to check your phone every minute. Even if you get dozens of notifications in between, you only look when the minute timer goes off.
Summary
Debouncing waits for a quiet period before running a function. Each time the button is clicked, a timer is started (or restarted). If no additional clicks happen during that timer window (say 3 seconds), the API call executes. If another click occurs before the delay elapses, the timer resets and the countdown starts over. This ensures that the API is called only once after the user has stopped clicking.
Throttling allows the action to happen immediately, but then suppresses additional triggers for a set period. On the first click, the API call runs right away. For the next few seconds (again, say 3 seconds), any further clicks are ignored. Only after that interval has passed will a new click result in another API call. This guarantees that the function is called at most once per interval, regardless of how often the user clicks.
Debounce: Imagine clicking a button that triggers an API call. When the function is debounced, a delay is introduced between the moment the user interacts and the moment the call actually fires. For example, if you set a delay of three seconds, the API call will execute only after three seconds have passed without any further clicks. If the user clicks again before those three seconds are up, the delay resets, and the function waits for another full three seconds from the most recent click before firing.
Throttle: With throttling, the first click triggers the API call immediately, just as it normally would. However, subsequent clicks are ignored until a set period has elapsed. Continuing the three-second example, after the initial call runs, no additional calls will occur until three seconds have passed. Only then will another click trigger a new API call.
- Debouncing reduces call count but adds delay. Throttling provides regular updates but may drop events.
- Both patterns are typically implemented using
setTimeoutand timestamps.
Common misconceptions
- "Debounce and throttle are the same." They both rate-limit calls, but debounce waits for
inactivity while throttle guarantees periodic execution. - "Debounce functions always improve UX." Over-debouncing can make an interface feel
unresponsive, because the action waits until the user stops for long enough. - "Throttle always catches every event." Throttling discards events between intervals; if the
interval is too long, important changes may be skipped.
Practice questions
- Coding: Write your own
debouncefunction that accepts a callback and a delay, and
demonstrate it with akeyuphandler that sends an API request only when the user stops typing. - Coding: Implement a
throttlefunction that ensures a callback fires once every 200 ms, and
test it on ascrollevent. - Theory: Describe a scenario where debouncing would be inappropriate but throttling would
work well, and vice versa.