Debouncing
Introduction
Debouncing delays a function until calls have stopped for a specified amount of time. If a user triggers an event repeatedly, only the final call after the quiet period runs.
In interviews, debounce is the canonical timer machine-coding question. A strong answer handles trailing execution, optional leading execution, argument preservation, this, and cancellation.
Why This Matters
Debouncing protects expensive work from bursty input: search boxes, resize recalculation, autosave, validation, and analytics. It tests whether you can reason about asynchronous timers and state that survives across calls.
Theory
Mental model
Each call resets a timer. The wrapped function runs only if no new call arrives before the timer expires. That is why debounce is ideal for wait-until-the-user-pauses behavior.
Trailing vs leading
Trailing debounce runs after the quiet period with the latest arguments. Leading debounce runs immediately on the first call in a burst, then suppresses later calls until the wait window closes. Some utilities support both, but the implementation must avoid double-calling a single isolated invocation.
Debounce vs throttle
Debounce waits for silence. Throttle allows execution at a maximum rate. For a search input, debounce usually makes sense. For scroll progress, throttle usually makes sense.
Implementation state
A debounce wrapper stores a timer id, latest arguments, and latest receiver. On every call, it clears the old timer and schedules a new one.
Visual Diagrams
calls: A ---- B ---- C ---------------- D wait: reset reset run C after quiet run D output: C D
Only the last call in each burst survives the quiet period.
Code Examples
Debounce with leading and trailing options
This implementation is framework-agnostic and works anywhere setTimeout exists.
Playground
Press Run to execute the code and see output here.
Output Prediction
Predict the output #1
1function debounce(fn, wait) {2 var timer = null;3 return function (value) {4 clearTimeout(timer);5 timer = setTimeout(function () {6 fn(value);7 }, wait);8 };9}10 11var log = debounce(function (value) {12 console.log(value);13}, 20);14 15log('A');16setTimeout(function () { log('B'); }, 5);17setTimeout(function () { log('C'); }, 10);18setTimeout(function () { log('D'); }, 40);19setTimeout(function () { console.log('done'); }, 80);Coding Exercises
Build debounce with cancel and flush
HardImplement debounce(fn, wait, options) with trailing execution by default, optional leading, plus .cancel() and .flush() methods. Preserve the latest arguments and this value.
Interview Questions
1Explain debounce vs throttle with an example for each.
Debounce waits until calls stop, fitting search suggestions after typing pauses. Throttle runs at most once per interval, fitting scroll or resize progress updates during continuous activity.
Follow-ups
- How do leading and trailing debounce differ?
- How would you add cancel support?
2Why must debounce store the latest arguments?
The call that finally runs should represent the most recent event in the burst. Without latest arguments and receiver, the delayed call can execute with stale data or the wrong this value.
Quiz
1. A trailing debounce is best described as:
Summary
- Debounce waits for a quiet period before invoking the function.
- Trailing debounce runs with the latest arguments after the burst.
- Leading debounce runs at the start of a burst and suppresses later calls during the wait window.
- A complete implementation preserves `this`, supports cancellation, and avoids stale arguments.
Cheat Sheet
Debounce: reset timer on every call.
Trailing: run after silence with latest args.
Leading: run immediately on first call in burst.
Use for: search input, autosave, validation, resize end.
State: timer id, latest args, latest receiver.