TK
All writing
Frontend

Debounce vs throttle (with real code)

Two techniques everyone confuses. What each does, the difference in one sentence, when to use which (search inputs vs scroll handlers), and copy-paste implementations.

Tanveer Khan7 min read

Debounce and throttle both limit how often a function runs, which is why they get mixed up. The difference is simple once you see it: debounce waits for quiet; throttle enforces a steady rate.

The one-sentence difference

  • Debounce: run only AFTER the events stop for X ms. (Wait until the user finishes.)
  • Throttle: run at most once every X ms while events keep firing. (Steady drip.)

Debounce — for 'when they're done'

Search-as-you-type: don't hit the API on every keystroke, only once typing pauses.

debounce.ts
ts
function debounce<T extends (...a: any[]) => void>(fn: T, ms: number) {
  let t: ReturnType<typeof setTimeout>;
  return (...args: Parameters<T>) => {
    clearTimeout(t);
    t = setTimeout(() => fn(...args), ms);
  };
}

const search = debounce((q: string) => api.search(q), 300);
input.addEventListener("input", (e) => search(e.target.value));

Throttle — for 'at a steady rate'

Scroll or resize handlers: run periodically as it fires, not hundreds of times a second.

throttle.ts
ts
function throttle<T extends (...a: any[]) => void>(fn: T, ms: number) {
  let last = 0;
  return (...args: Parameters<T>) => {
    const now = Date.now();
    if (now - last >= ms) {
      last = now;
      fn(...args);
    }
  };
}

const onScroll = throttle(() => updateScrollProgress(), 100);
window.addEventListener("scroll", onScroll);

Rule of thumb: debounce for 'the user finished' (search box, auto-save, resize-then-recalculate). Throttle for 'keep up but not too often' (scroll position, mousemove, firing analytics).

Debounce collapses a burst into one call after it settles; throttle caps the rate during the burst. Pick by asking whether you want the final result or a regular sample.