Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Debounce in Javascript

I am currently learning debounce in Javascript and I came across two ways of writing debounce functions that works the same. One is a lot simpler like regular function, the other one is the complicated one that everyone seems to use.

Version 1:

<input type="text" oninput="betterFunction()">

<script>
function getData() {
  console.log('Data Fetched')
}

function debounce(callback, delay) {
  let timer
  return function() {
    clearTimeout(timer)
    timer = setTimeout(() => {
      callback();
    }, delay)
  }
}

const betterFunction = debounce(getData, 1000)
</script>

Version 2:

<input type="text" oninput="debounce()">

<script>
let timer

function debounce() {
  clearTimeout(timer)
  timer = setTimeout(() => {
    console.log('Data Fetched');
  }, 1000)
}
</script>

What is the difference between these two ways of debouncing if they both gives the same result? PS: I am surprised that I have never seen anyone use the 'version 2', ofcourse something must me wrong. Could anybody explain the differences please?

like image 787
Voodoo Child Avatar asked Sep 05 '25 03:09

Voodoo Child


1 Answers

Version 1 is better, because it:

  • encapsulates the timer (timeout ID) within the scope of—the lifetime of—the function call
  • eliminates nasty global variables which could accidentally be modified outside or within another function
  • most of all, it is reusable

An even-better debounce

Josh W Comeau has an informative article covering debounce.

Here is his (modified) minimal version:

const debounce = (callback, wait) => {
  let timeoutId = null;
  return (...args) => {
    window.clearTimeout(timeoutId);
    timeoutId = window.setTimeout(() => {
      callback(...args);
    }, wait);
  };
}

Note: I replaced the callback.apply(null, args) with the more succinct callback(...args)

Usage

const handleMouseMove = debounce((mouseEvent) => {
  // Do stuff with the event!
}, 250);

document.addEventListener('mousemove', handleMouseMove);    // Add listener
document.removeEventListener('mousemove', handleMouseMove); // Remove listener

Snippet

In the snippet below, points are drawn every time the user stops moving the mouse after 250ms. Each point is auto-removed after 2 seconds.

const debounce = (callback, wait) => {
  let timeoutId = null;
  return (...args) => {
    window.clearTimeout(timeoutId);
    timeoutId = window.setTimeout(() => {
      callback(...args);
    }, wait);
  };
}

const createPoint = (x, y, color) => {
  const point = Object.assign(document.createElement('div'), { className: 'point' });
  Object.assign(point.style, { top: `${y - 2}px`, left: `${x - 2}px`, background: color });
  document.body.append(point);
  return point;
};

// Log mouse coordinates when user stops moving mouse after 250ms
const handleMouseMove = debounce(({ clientX: x, clientY: y }) => {
  console.log(`Creating MOVE point at: (${x}, ${y})`);
  const point = createPoint(x, y, 'white');
  // Auto-remove after 1 second
  setTimeout(() => {
    console.log(`Removing MOVE point at: (${x}, ${y})`);
    point.remove();
  }, 2000);
}, 250);

// Log mouse coordinates when user stops clicking 250ms
const handleClick = debounce(({ clientX: x, clientY: y }) => {
  console.log(`Creating CLICK point at: (${x}, ${y})`);
  const point = createPoint(x, y, 'red');
  // Auto-remove after 1 second
  setTimeout(() => {
    console.log(`Removing CLICK point at: (${x}, ${y})`);
    point.remove();
  }, 2000);
}, 250);

document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('click', handleClick);
.as-console-wrapper { max-height: 5em !important; }
html, body { width: 100%; height: 100%; margin: 0; padding: 0; }
body { background: #222; position: relative; }
.point { position: absolute; width: 4px; height: 4px; border-radius: 50%; background: #FFF; }
like image 185
Mr. Polywhirl Avatar answered Sep 07 '25 23:09

Mr. Polywhirl