All questions
Medium2026-08-14

Implement Advanced Debounce with Leading Edge and Cancel

Companies
FlipkartRazorpay
Role

Senior SDE (Frontend)

Round

Round 2 (JavaScript Deep Dive)

JavaScriptClosuresTimersEvent Handling

Problem Statement

In high-frequency event contexts (search input streams, window resize handlers, scroll events), debouncing is essential to avoid excessive function calls.

Implement a custom debounce function with the following capabilities:

const debounced = debounce(fn, delay, options);

Requirements

  1. Core behavior: The returned function delays invoking fn until delay ms have elapsed since the last call.
  2. Context & Arguments: Preserve this binding and forward all arguments to fn.
  3. Leading edge execution (options.immediate):
    • If true, trigger fn immediately on the first call
    • Subsequent calls during the delay window are suppressed
    • After the delay expires with no calls, the next call triggers immediately again
  4. Cancellation: The returned function must have a .cancel() method that clears any pending timer and resets internal state.

Constraints

  • No external libraries (no Lodash)
  • Must work in both browser and Node.js environments
  • Should handle rapid successive calls correctly
  • Edge case: calling .cancel() when nothing is pending should not throw

Example

const log = debounce(console.log, 300);

log("a");  // nothing yet
log("b");  // resets timer
log("c");  // resets timer
// 300ms later → logs "c"

const logImmediate = debounce(console.log, 300, { immediate: true });

logImmediate("x");  // logs "x" immediately
logImmediate("y");  // suppressed
logImmediate("z");  // suppressed
// 300ms of silence...
logImmediate("w");  // logs "w" immediately (delay expired)

logImmediate.cancel();  // clears any pending execution

What the Interviewer Expects

  1. Closure understanding — timer ID and state must persist across calls via closure.
  2. this binding — use fn.apply(context, args) or arrow functions correctly. Common trap: arrow functions inside debounce lose the caller's this.
  3. Leading vs trailing edge logic:
    • Trailing (default): set timer on each call, only the last one fires
    • Leading: fire immediately if no timer is active, then start suppression window
  4. Cancel implementation: clearTimeout(timerId) + reset the timer variable.
  5. Return value handling (bonus): for immediate mode, the debounced function could return fn's result.

Follow-ups

  1. How would you add a flush() method that executes the pending function immediately?
  2. What's the difference between debounce and throttle? When would you use each?
  3. How would you implement a maxWait option — guarantee fn is called at least once every N ms even if events keep coming?
  4. How does React's useCallback interact with debounced functions? What's the common bug?
  5. How would you unit test this? What timing strategies would you use (fake timers)?
🧠

No solution provided

Think through it. That's how you build real interview muscle.

Share: