javascriptdebouncing

What is the "debounce" function in JavaScript?


I am interested in the "debouncing" function in JavaScript, at JavaScript Debounce Function.

Unfortunately the code is not explained clearly enough for me to understand. How does it work (I left my comments below)? In short, I just really do not understand how this works.

   // Returns a function, that, as long as it continues to be invoked, will not
   // be triggered. The function will be called after it stops being called for
   // N milliseconds.


function debounce(func, wait, immediate) {
    var timeout;
    return function() {
        var context = this, args = arguments;
        var later = function() {
            timeout = null;
            if (!immediate) func.apply(context, args);
        };
        var callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func.apply(context, args);
    };
};

The copied code snippet previously had callNow in the wrong spot.


Solution

  • The code in the question was altered slightly from the code in the link. In the link, there is a check for (immediate && !timeout) before creating a new time-out. Having it after causes immediate mode to never fire. I have updated my answer to annotate the working version from the link.

    function debounce(func, wait, immediate) {
      // 'private' variable for instance
      // The returned function will be able to reference this due to closure.
      // Each call to the returned function will share this common timer.
      var timeout;
    
      // Calling debounce returns a new anonymous function
      return function() {
        // reference the context and args for the setTimeout function
        var context = this,
          args = arguments;
    
        // Should the function be called now? If immediate is true
        //   and not already in a timeout then the answer is: Yes
        var callNow = immediate && !timeout;
    
        // This is the basic debounce behaviour where you can call this
        //   function several times, but it will only execute once
        //   (before or after imposing a delay).
        //   Each time the returned function is called, the timer starts over.
        clearTimeout(timeout);
    
        // Set the new timeout
        timeout = setTimeout(function() {
    
          // Inside the timeout function, clear the timeout variable
          // which will let the next execution run when in 'immediate' mode
          timeout = null;
    
          // Check if the function already ran with the immediate flag
          if (!immediate) {
            // Call the original function with apply
            // apply lets you define the 'this' object as well as the arguments
            //    (both captured before setTimeout)
            func.apply(context, args);
          }
        }, wait);
    
        // Immediate mode and no wait timer? Execute the function...
        if (callNow) func.apply(context, args);
      }
    }
    
    /////////////////////////////////
    // DEMO:
    
    function onMouseMove(e){
      console.clear();
      console.log(e.x, e.y);
    }
    
    // Define the debounced function
    var debouncedMouseMove = debounce(onMouseMove, 50);
    
    // Call the debounced function on every mouse move
    window.addEventListener('mousemove', debouncedMouseMove);