Glossary

Debounce

Debounce is a technique that delays running a function until calls have stopped arriving for a set wait time, so a burst of events produces a single invocation. You meet it most often in search boxes and other handlers for user input. ECMA-262 defines no debounce function, and in Node.js 22 typeof debounce is undefined, so you write it with setTimeout or import one from a utility library.

How it works

A debounced function wraps the real one and owns a single timer. Each call cancels the pending timer and starts a new one with the wait value in milliseconds. Only when the timer survives the full wait without being reset does the real function run.

  • Trailing edge: the real function runs once after the last call plus the wait. This is the usual default and the one search boxes want.
  • Leading edge: the real function runs on the first call of a burst and later calls inside the wait are ignored. Useful when the first event needs an immediate response.
  • Both edges: some implementations run on both, and then usually stop the next leading call from firing until the wait has passed after the previous trailing call.

The timer state lives in a closure, so every caller must share the same wrapper. The wrapper also has to forward the arguments and this of the last call, because earlier calls in the burst are discarded.

function debounce(fn, wait) {
  let timer;
  return function (...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), wait);
  };
}
const search = debounce((q) => console.log('search:', q), 200);
const keys = ['j', 'js', 'jso', 'json'];
keys.forEach((q, i) => setTimeout(() => { console.log('keystroke:', q); search(q); }, i * 50));
setTimeout(() => { console.log('keystroke:', 'json f'); search('json f'); }, 600);

Output from Node 22:

keystroke: j
keystroke: js
keystroke: jso
keystroke: json
search: json
keystroke: json f
search: json f

Five keystrokes produced two searches. The first four were 50 ms apart, inside the 200 ms wait, so only the last one ran, 200 ms after it.

What is the difference between debounce and throttle?

Debounce waits for activity to stop, while Throttle runs the function at a steady maximum rate during activity. A debounced scroll handler stays silent until the user stops scrolling. A throttled one fires, say, every 100 ms while they scroll. Pick debounce when only the final value matters (search text, form validation) and throttle when you need updates along the way (scroll position, drag).

Common pitfalls

  • Creating the wrapper on every event: calling debounce inside the handler gives each event its own timer, so nothing is merged. In a test with three calls, a wrapper created per call ran 3 times and one shared wrapper ran once. Create it once, outside the handler or in a memo or ref.
  • Losing a pending call on exit: a debounced save with a 1000 ms wait never runs if the page unloads or the code calls process.exit() 400 ms later. For autosave, flush or cancel the pending call when the page hides or the component unmounts.
  • Wait value too long: the real function runs only after the full wait of silence, so the wait adds directly to the delay the user sees. Tune it per use case instead of reusing one value everywhere.
  • Dropping arguments: only the last call's arguments reach the real function. Calling a debounced logger with 1, 2 and 3 printed only 3. If each call carries data you need, such as a list of changed ids, collect it yourself instead of debouncing.
  • Leading edge only misses the final state: with leading-only, a burst ending in a different value is never processed. A leading-only wrapper called with a, b and c ran once, with a. Use both edges when the last value matters.
  • Reading stale DOM state: a handler that runs later sees the DOM as it is then, not as it was at the first event. Read values at call time and pass them in.

Related terms

  • Throttle — caps how often a function runs during a burst instead of waiting for it to end
  • DOM — the source of most events that get debounced, such as input, scroll and resize
  • HTTP — requests triggered from debounced handlers are a common reason to debounce

See also

  • Term: Throttle — the rate-limiting alternative, with the key difference in when the function fires