FossilsðŸ’ŧ CodingImplement Throttle
ðŸĢHatchlingJavaScriptUtilityPerformance

Implement Throttle

Throttle guarantees a function runs at most once per interval. Implementation reveals your understanding of timing, state management, and edge cases.

Implement Throttle

Interview Question: "Implement a throttle function that ensures fn is called at most once every interval milliseconds."

The Core Implementation

function throttle(fn, interval) {
  let lastTime = 0;
 
  return function(...args) {
    const now = Date.now();
    if (now - lastTime >= interval) {
      lastTime = now;
      fn.apply(this, args);
    }
  };
}

Production Version (Leading + Trailing)

The basic version only fires on the leading edge. A proper throttle fires on both leading and trailing edges:

function throttle(fn, interval) {
  let lastTime = 0;
  let timeoutId;
 
  return function(...args) {
    const now = Date.now();
    const remaining = interval - (now - lastTime);
 
    if (remaining <= 0) {
      clearTimeout(timeoutId);
      timeoutId = undefined;
      lastTime = now;
      fn.apply(this, args);
    } else if (!timeoutId) {
      timeoutId = setTimeout(() => {
        lastTime = Date.now();
        timeoutId = undefined;
        fn.apply(this, args);
      }, remaining);
    }
  };
}

Why the trailing call matters: If the user scrolls and stops mid-interval, the last scroll position should still be processed.

Debounce vs Throttle — The Key Distinction

Input:    ──x─x─x─x─x─x─x─────────
Debounce: ──────────────────x─────── (fires once after silence)
Throttle: ──x─────x─────x─────x──── (fires at regular intervals)
  • Debounce: "Wait until they stop." → Search input, resize handler, auto-save
  • Throttle: "Run at most N times per second." → Scroll tracking, mouse move, rate limiting

Common Mistakes

  • Only implementing leading edge (missing trailing call)
  • Using setInterval instead of setTimeout (wrong mental model — throttle is per-call, not periodic)
  • Not preserving this and arguments