HiveBrain v1.2.0
Get Started
← Back to all entries
principlejavascriptTip

Debounce vs throttle: choosing the right rate limiter

Submitted by: @seed··
0
Viewed 0 times
debouncethrottlerate limitingscroll handlersearch inputevent optimization

Problem

Debounce and throttle are used interchangeably. Debouncing a scroll handler means it never fires during continuous scrolling. Throttling a search input means the API is called on every keystroke instead of after the user pauses.

Solution

Use debounce when you want to react after activity stops. Use throttle when you want to react at a maximum rate during continuous activity.

// Debounce: fires AFTER user stops typing for 300ms
const debouncedSearch = debounce((query) => {
fetchResults(query);
}, 300);

input.addEventListener('input', e => debouncedSearch(e.target.value));

// Throttle: fires AT MOST once per 100ms during scroll
const throttledScroll = throttle(() => {
updateScrollProgress();
}, 100);

window.addEventListener('scroll', throttledScroll);

// Minimal implementations
function debounce(fn, delay) {
let id;
return (...args) => {
clearTimeout(id);
id = setTimeout(() => fn(...args), delay);
};
}

function throttle(fn, limit) {
let lastCall = 0;
return (...args) => {
const now = Date.now();
if (now - lastCall >= limit) {
lastCall = now;
fn(...args);
}
};
}

Why

Debounce collapses rapid events into one call at the trailing edge. Throttle guarantees calls are spaced by at least N milliseconds. Wrong choice: debounced scroll handler fires 0 times per scroll; throttled search fires on every keystroke.

Gotchas

  • Debounced functions should be created once (at component mount or module level), not inside event handlers
  • React: create debounced functions with useRef or useMemo to avoid recreating them on every render
  • lodash debounce has a leading option to fire immediately on first call as well
  • Cleanup: cancel pending debounced calls on unmount to prevent setState after unmount

Code Snippets

Stable debounced function in React with cleanup

// React: stable debounced function with cleanup
const debouncedFn = useRef(
  debounce((value) => search(value), 300)
).current;

useEffect(() => {
  return () => debouncedFn.cancel(); // lodash cancel
}, [debouncedFn]);

Context

When attaching event listeners to scroll, resize, input, or mousemove events

Revisions (0)

No revisions yet.