Deboucing

March 06, 2020

2 min read

The goal behind debouncing is to reduce the overhead by preventing a function from being called several times in succession. It is achieved by introducing a delay using setTimeout function.

const DELAY = 500; // ms

function searchData() {
  const searchInput = document.getElementById('search').value;
  if (searchInput) {
    // Call to search API
    console.log('Searching for...', searchInput);
  }
}

// Return an anomymous function that has access to the `fn`
// argument of our `debounce` method through closures.
function debounce(fn, delay) {
  let timer;

  return function () {
    //  Assign `this` to a variable named `context` so that the `fn` argument
    //  passed to our `debounce` method can be called in the proper context.
    const context = this;
    // Assign `arguments` to `args`, allows us to combine all arguments passed
    // in the `fn` argument of our `debounce` method in a single variable.
    const args = arguments;

    if (timer) {
      // As long as the event that our `debounce` method is bound to is still firing
      // within the `wait` period, remove the numerical ID (returned from `setTimeout`)
      // from JavaScript's execution queue. This prevents the function passed in the
      // `setTimeout` function from being invoked.
      clearTimeout(timer);
    }

    timer = setTimeout(() => {
      // `apply` method allows us to call a function in an explicit context.
      fn.apply(context, args);
    }, delay);
  };
}

const efficientSearchData = debounce(searchData, DELAY);
// ES6
const debounce = (func, delay) => {
  let timer;

  return (...args) => {
    const next = () => func(args);

    if (timer) {
      clearTimeout(timer);
    }

    timer = setTimeout(next, delay > 0 ? delay : DELAY);
  };
};

Test

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Test Debouce</title>
  </head>
  <body>
    <input type="text" oninput="efficientSearchData()" id="search" />
    <script src="./debounce.js"></script>
  </body>
</html>

With deboucing, the searchData function would be called only when the delay between two successive oninput events is at least 500ms.