Skip to content
JavaScript utilities · Core · about 25 minutes

Throttle

Implement throttle(fn, wait). The first call runs fn at once and opens a window of wait milliseconds. Calls inside the window do not run at once: the latest one is remembered and runs when the window ends (a trailing run), and that run opens a new window. If nothing came during a window, the next call runs at once again. Keep the arguments and the this of the call that runs. Scripts and answers use [time, argument], as in Debounce.

Start from

function throttle(fn, wait) {
  // Return a function that runs fn at most once per window.
}

Hints

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

  1. Hint 1

    You need two pieces of state: whether a window is open, and the latest call that is still waiting.

  2. Hint 2

    When the window's timer ends, close the window. If a call is waiting, run it, and running it opens the next window.

  3. Hint 3

    Put the run in one inner function (call fn, mark the window open, set the timer) and use it for both the first call and the trailing one.

Solution

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

JavaScript
function throttle(fn, wait) {
  let open = false;
  let waiting = null;
  function run(context, args) {
    fn.apply(context, args);
    open = true;
    setTimeout(() => {
      open = false;
      if (waiting) {
        const next = waiting;
        waiting = null;
        run(next.context, next.args);
      }
    }, wait);
  }
  return function (...args) {
    if (open) waiting = { context: this, args };
    else run(this, args);
  };
}

What an interviewer looks for

Scroll, resize and drag handlers need it. A good answer says what happens to the calls inside the window, because losing the last one is the usual bug.