CodePark/ questions / Functional patterns
START PRACTICINGPRACTICE
Frontend question · JavaScript › Functional patterns

Implement a debounce(fn, wait) function.

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

The debounced function keeps a timer id in a closure. On every call, it clears the waiting timer and starts a new one. So fn runs only after wait ms pass with no new call.

Two details make an answer stand out:

  • Use a regular function for the wrapper and call fn.apply(this, args). This passes on the original this and the latest arguments. It matters when the debounced function is used as a method.
  • The arrow function inside setTimeout takes that this from the wrapper, because arrows have no this of their own.

Common extensions:

  • a cancel() method that clears the timer, useful when a component unmounts
  • a leading option that runs fn on the first call
  • a maxWait option that forces a run during a never-ending stream of calls
  • Store the timer id in a closure.
  • On every call, run clearTimeout, then setTimeout.
  • Pass on this and the latest arguments with apply.
  • Common extensions are cancel, a leading edge and maxWait.