Skip to content
JavaScript utilities · Core · about 20 minutes

Debounce

Implement debounce(fn, wait). It returns a function. Every call restarts the wait; when wait milliseconds pass with no new call, fn runs once, with the arguments and the this of the last call. The returned function also has cancel(), which drops a waiting run. Use setTimeout and clearTimeout directly. In the tests, a script lists calls as [time, argument], and the answer lists each real run of fn as [time, argument].

Start from

function debounce(fn, wait) {
  // Return a function that waits for the calls to stop.
}

Hints

Each hint gives away a little more. Open one only when you need it.

  1. Hint 1

    Keep one timer id in the closure. Every call clears it and sets a new one.

  2. Hint 2

    Use a regular function for the returned function, so this is the caller's this, and pass it on with fn.apply(this, args).

  3. Hint 3

    cancel is a property on the returned function: debounced.cancel = () => clearTimeout(timer).

Solution

From here on the page gives the method away. If you are still solving, go back to the hints.

JavaScript
function debounce(fn, wait) {
  let timer;
  function debounced(...args) {
    clearTimeout(timer);
    timer = setTimeout(() => {
      timer = undefined;
      fn.apply(this, args);
    }, wait);
  }
  debounced.cancel = () => {
    clearTimeout(timer);
    timer = undefined;
  };
  return debounced;
}

What an interviewer looks for

It shows closures and timers in one small function. Interviewers then ask about this, the arguments of the last call, and how you would cancel it when a component unmounts.