Debouncing delays execution until a pause in activity while throttling limits execution to fixed intervals, and choosing the right technique for each use case is a frequent interview question and essential for responsive UIs.
Delays execution until a pause in activity. Each event resets the timer. Ideal for search input, auto-save, and window resize handlers.
Limits execution to at most once per interval during ongoing activity. Ideal for scroll handlers, drag events, and real-time UI updates.
Debounce when you need the final value after activity stops. Throttle when you need regular updates during ongoing activity.
Both techniques support executing on the leading edge (immediately) or trailing edge (after the delay), configurable in lodash and custom implementations.
Debouncing and throttling are rate-limiting techniques that control how frequently a function executes in response to rapid, repeated events. Without them, handlers for events like scroll, resize, input, and mousemove can fire dozens or hundreds of times per second, overwhelming the main thread and causing janky, unresponsive interfaces.
Debouncing delays function execution until a specified period of inactivity has passed. Every time the event fires, the timer resets. The function only executes once, after the user stops triggering the event.
The classic use case is search-as-you-type: you do not want to fire an API request for every keystroke. Instead, debounce with a 300ms delay — the request fires only after the user pauses typing for 300ms.
function debounce(fn, delay) {
let timeoutId;
return function (...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn.apply(this, args), delay);
};
}
const handleSearch = debounce((query) => {
fetch(`/api/search?q=${query}`);
}, 300);The key characteristic: debouncing waits for the activity to stop. If events keep firing, the function keeps getting delayed. This means in a continuous stream of events, the function may never execute until the stream ends.
Throttling guarantees the function executes at most once per specified time interval, regardless of how many times the event fires. Unlike debouncing, throttled functions execute periodically during ongoing activity.
The classic use case is scroll handlers: you want to update a progress bar or check scroll position during scrolling, but not on every single scroll event (which can fire at 60+ times per second). Throttling to once every 100ms gives smooth updates without overwhelming the browser.
function throttle(fn, interval) {
let lastTime = 0;
return function (...args) {
const now = Date.now();
if (now - lastTime >= interval) {
lastTime = now;
fn.apply(this, args);
}
};
}
const handleScroll = throttle(() => {
updateProgressBar();
}, 100);The key characteristic: throttling ensures regular execution during activity. The function runs at fixed intervals no matter how fast events fire.
Debounce when you want the final value after activity stops: search input, form validation, window resize calculations, auto-save.
Throttle when you want regular updates during ongoing activity: scroll position tracking, drag-and-drop, mousemove handlers, rate-limiting API calls, real-time UI updates.
A helpful mental model: debounce is "wait until they stop," throttle is "do it at most every N milliseconds."
Both techniques can be configured for leading edge (execute immediately, then wait), trailing edge (wait, then execute), or both. Lodash's _.debounce and _.throttle support { leading: true, trailing: true } options. Leading debounce is useful for button clicks (respond immediately, ignore rapid re-clicks).
In React, wrap debounced/throttled handlers in useRef or useCallback to maintain the same instance across renders. Libraries like use-debounce and usehooks-ts provide ready-made React hooks. Note that useMemo and useCallback are not the same as debounce/throttle — they prevent unnecessary recalculation but do not control event frequency.
For visual updates, requestAnimationFrame is a natural throttle to the display's refresh rate (typically 60fps = ~16ms). It ensures updates happen in sync with the browser's paint cycle, producing smoother animations than arbitrary throttle intervals.
Debounce waits for a pause — the function runs once after activity stops. Throttle runs at regular intervals during activity. The choice depends on whether you need the final result (debounce) or periodic updates (throttle).
Fun Fact
The term 'debounce' comes from electrical engineering. Physical buttons and switches 'bounce' when pressed, rapidly toggling between on and off states for a few milliseconds. Hardware debouncing circuits and software debounce algorithms were invented to ignore these bounces and register a single clean press.