David John Thammineni
JavaScript28 Jul 20202 min read

Debounce vs. Throttle: Pick the Right Tool

Debounce vs. Throttle: Pick the Right Tool

Every frontend developer eventually types "debounce vs throttle" into a search engine at 6pm on a deadline. Here's the version I wish I'd read first.

The one-line distinction

  • Debounce: run the function once, after the calls stop for N ms. Good for "wait until the user is done".
  • Throttle: run the function at most once every N ms while calls continue. Good for "keep up, but at a sane rate".

Debounce for search-as-you-type

function debounce(fn, delay) {
  let timer;
  return function (...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
}

searchInput.addEventListener(
  "input",
  debounce((e) => fetchSuggestions(e.target.value), 300)
);

Each keystroke resets the timer, so the API call only fires 300ms after the user pauses. Ten keystrokes, one request.

Throttle for scroll and resize

function throttle(fn, interval) {
  let last = 0;
  return function (...args) {
    const now = Date.now();
    if (now - last >= interval) {
      last = now;
      fn.apply(this, args);
    }
  };
}

window.addEventListener(
  "scroll",
  throttle(updateProgressBar, 100)
);

A debounced scroll handler would only fire when scrolling stops — useless for a reading-progress bar. Throttle keeps it updating smoothly at 10fps instead of hammering it at every scroll event.

Choosing in practice

Ask: do I care about the final state (debounce) or the ongoing state (throttle)? Autosave: debounce. Infinite-scroll position check: throttle. Window resize re-layout: usually debounce, because you only care where it lands. And if you're already shipping lodash, _.debounce and _.throttle handle edge cases (leading/trailing invocation) that these minimal versions skip.

Keep Reading