en.javascript.info/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/solution.md
Laurent Lyaudet c2cbc4cdaa
avoid race condition in 06-advanced-functions/09-call-apply-decorators/04-throttle/solution.md
Well there is still a chance for a race condition but a smaller one.
We would need an atomic "if or set" for isThrottled
2021-03-24 17:27:49 +01:00

1.2 KiB

function throttle(func, ms) {

  let isThrottled = false,
    savedArgs,
    savedThis;

  function wrapper() {

    if (isThrottled) { // (2)
      savedArgs = arguments;
      savedThis = this;
      return;
    }
    isThrottled = true;

    func.apply(this, arguments); // (1)

    setTimeout(function() {
      isThrottled = false; // (3)
      if (savedArgs) {
        wrapper.apply(savedThis, savedArgs);
        savedArgs = savedThis = null;
      }
    }, ms);
  }

  return wrapper;
}

A call to throttle(func, ms) returns wrapper.

  1. During the first call, the wrapper just runs func and sets the cooldown state (isThrottled = true).
  2. In this state all calls are memorized in savedArgs/savedThis. Please note that both the context and the arguments are equally important and should be memorized. We need them simultaneously to reproduce the call.
  3. After ms milliseconds pass, setTimeout triggers. The cooldown state is removed (isThrottled = false) and, if we had ignored calls, wrapper is executed with the last memorized arguments and context.

The 3rd step runs not func, but wrapper, because we not only need to execute func, but once again enter the cooldown state and setup the timeout to reset it.