Throttle vs Debounce

April 15, 2018

Throttle and debounce are two ways to limit the number of times a function is executed over a period for performance reasons. For example, you may not want to execute a callback on every scroll event but instead just when the user has finished scrolling.

What’s the difference?

My favorite exaplanation has been Chris Coyier’s post.

Here’s some code that illustrates the difference:

/**
 * Debouncing enforces that a function not be called again until a certain amount of time has passed
 * without it being called. As in "execute this function only if 100 milliseconds have passed without it being called."
 * @param func
 * @param timeout
 * @returns {Function}
 */
function debounce(func, timeout) {
  let timeoutID;
  return function (...args) {
    const context = this;
    clearTimeout(timeoutID)
    timeoutID = setTimeout(() => func.apply(context, args), timeout);
  };
}

/**
 * Throttles a function and delays its execution, so it's only called at most
 * once within a given time period.
 *
 * @param {Function} fn The function to throttle.
 * @param {number} timeout The amount of time that must pass before the
 *     function can be called again.
 * @return {Function} The throttled function.
 */
function throttle(fn, timeout) {
  let timer = null;
  return function (...args) {
    if (!timer) {
      timer = setTimeout(function () {
        fn(...args);
        timer = null;
      }, timeout);
    }
  };
}

// The code below shows how these functions can be used and their different outputs
function mySlowFunction(i) {
  console.log('hello', i);
}
let debouncedFunc = debounce(mySlowFunction, 100);
let throttledFunc = throttle(mySlowFunction, 100);

for (let i = 1; i <= 10; i++) {
  debouncedFunc(i);
}
// this outputs: 'hello 10' because the frequent calls reset the debounce timer

for (let i = 1; i <= 10; i++) {
  throttledFunc(i);
}
// this outputs: '1' because it's the first execution allowed within the timeout, all others are ignored