Debounce vs Throttle
Both limit how often a function executes, but debounce waits until activity stops while throttle enforces a steady maximum rate.
Side-by-Side Comparison
| Feature | Debounce | Throttle |
|---|---|---|
| Execution timing | After activity stops | At regular intervals during activity |
| During rapid calls | Keeps resetting, only last fires | Fires once per interval |
| Guaranteed execution | Only after pause | At steady intervals |
| First call | Delayed | Immediate (leading edge) |
Code Examples
Debounce
- Delays execution until after a pause in activity
- Resets the timer on every new call
- Only the LAST call in a burst actually executes
- Best for "wait until the user is done" scenarios
function debounce(fn, delay) {
let timer
return (...args) => {
clearTimeout(timer)
timer = setTimeout(() => fn(...args), delay)
}
}
// User types "hello" — only fires ONCE after typing stops
input.addEventListener('input',
debounce(handleSearch, 300)
)Throttle
- Executes at most once per time interval
- Guarantees regular execution during sustained activity
- First call executes immediately (leading edge)
- Best for "limit the rate" scenarios
function throttle(fn, limit) {
let inThrottle = false
return (...args) => {
if (!inThrottle) {
fn(...args)
inThrottle = true
setTimeout(() => inThrottle = false, limit)
}
}
}
// Fires at most every 200ms during scrolling
window.addEventListener('scroll',
throttle(handleScroll, 200)
)When to Use Which
Debounce
Search input, form validation, window resize handler, auto-save — anything where you want to wait until the user finishes.
Throttle
Scroll handlers, mousemove tracking, rate-limited API calls, game input — anything where you need regular updates during continuous activity.
Common Mistakes
- Using debounce for scroll handlers — causes janky UX because no updates happen until scrolling stops
- Using throttle for search inputs — fires unnecessary intermediate searches before the user finishes typing
- Forgetting to cancel debounce on component unmount — leads to state updates on unmounted components
Interview Questions
What happens if you debounce a scroll handler?
The handler only fires after the user stops scrolling, which creates a poor UX — no visual updates during scrolling, then a sudden jump. Throttle is the right choice for scroll because it provides regular updates at a controlled rate.
Implement debounce from scratch
Return a wrapper function that clears any existing timeout and sets a new one. The key insight is that clearTimeout(timer) on each call resets the delay, so only the last call in a burst survives to execute.
How do leading and trailing edge throttle differ?
Leading edge executes on the first call then blocks. Trailing edge blocks then executes at the end. Lodash's throttle supports both via options: { leading: true, trailing: true }.