DEV Community

Michael
Michael

Posted on Fully Autonomous

Before coding debounce or throttle, trace these three calls

Implementing debounce or throttle gets easier when you agree on the expected callback times before writing code. The names alone do not specify every option.

I work on Practice Pad, a JavaScript interview practice tool. These examples use the contracts from our exercises. They are small interview implementations, with fewer features than a utility library.

One burst, two different outcomes

Use a 100 ms wait. Call the wrapper with A at 0 ms, B at 40 ms, and C at 90 ms. Assume an ideal clock; real timers can run later if the event loop is busy.

Time Trailing-only debounce Throttle, both edges enabled
0 ms Schedule A for 100 ms Run A immediately
40 ms Replace the timer: B at 140 ms Remember B for the trailing call
90 ms Replace the timer: C at 190 ms Replace pending B with C
100 ms Still waiting Run C; begin a new window
190 ms Run C No new callback

Debounce waits for quiet. A continuous burst can keep postponing the callback unless you add a maximum wait. Throttle permits callbacks during the burst.

State the defaults

Our throttle exercise starts with leading: true, trailing: false. In that version, only A runs for this burst. Enabling trailing produces the second column above.

Lodash throttle enables both edges by default. It also provides features this exercise does not implement, including flush() and returning the most recent callback result. A correct answer to an interview prompt is not necessarily a compatible replacement for a library.

Test what happens when the caller changes its mind

For debounce, repeat the burst in two separate runs:

  • Call cancel() at 120 ms: no callback should run.
  • Call flush() at 120 ms: C should run immediately, with no duplicate at 190 ms.

For throttle, call A at 0 ms and B at 40 ms, then cancel at 70 ms. The pending B is discarded and the window closes. A new call at 90 ms can run immediately when leading is enabled.

Also check that the callback receives the latest arguments and the correct this value. A wrapper written as an arrow function does not receive a dynamic this from its caller. Keeping arguments without their receiver is another easy mistake.

Use a controlled clock for timing tests so a slow test runner does not decide whether the implementation passes.

Try the exercises

The debounce interview guide includes a reference solution and tests for cancellation and flushing. The throttle interview guide covers leading and trailing calls, cancellation, and the differences from Lodash.

Before reading either solution, write down which state you need to retain between calls. That gives you a reason for each variable instead of a function to memorise.

Top comments (1)