Learn the difference between debounce and throttle in JavaScript with runnable examples. When to use each, and how to implement both from scratch.
Debounce and throttle are two ways to control how often a function runs when events fire rapidly — keystrokes, scroll, resize, mousemove. Debounce waits until events stop: the function runs once after a quiet period. Throttle caps the rate: the function runs at most once every N milliseconds, regardless of how many events fire. Choosing the right one depends on whether you care about the final state (debounce) or sampled updates along the way (throttle).
Every call resets a timer. The function only fires when the timer elapses without being reset. Result: one call after the user stops typing, scrolling, or resizing.
The function runs immediately, then ignores subsequent calls until the interval has passed. Result: regular samples at a fixed rate, regardless of event frequency.
Both patterns can be configured to fire at the start of a burst (leading), the end (trailing), or both. Most libraries default debounce to trailing and throttle to leading+trailing.
Both implementations schedule timers. If a component unmounts or the listener is removed, cancel any pending timer to avoid calling a stale function with stale state.
Use debounce for 'final state' operations: autocomplete search, form validation after typing stops, saving drafts. Use throttle for 'live feedback' operations: scroll position reporting, mousemove handlers, resize layout updates.
function debounce(fn, delay) {
let timer = null;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
function throttle(fn, interval) {
let lastCall = 0;
return function (...args) {
const now = Date.now();
if (now - lastCall >= interval) {
lastCall = now;
fn.apply(this, args);
}
};
}
const log = label => value => console.log(label, value);
const debounced = debounce(log("debounced:"), 300);
const throttled = throttle(log("throttled:"), 300);
// Simulate rapid events
[0, 50, 100, 150, 200, 500, 900].forEach((ms, i) => {
setTimeout(() => {
debounced(i);
throttled(i);
}, ms);
});
Open this example in the TryJS playground to edit and run the code instantly in your browser — no signup needed.
Debounce fires once after events stop for a set duration. Throttle fires at most once per interval, regardless of how many events arrive. Debounce cares about the final event; throttle cares about sampling at a fixed rate.
Use debounce when you only care about the final value after a burst — search-as-you-type (wait until typing stops before hitting the API), resize handlers that recompute layout once the window settles, autosave drafts after idle.
Use throttle when you need regular sampled updates during a burst — scroll-position indicators, drag handlers, mousemove tracking, rate-limited API calls where you must send at most N requests per second.
No — both can be written in ~10 lines, as shown in the runnable example. Libraries add options (leading/trailing edges, maxWait, cancel methods) that are nice but not essential for simple cases.