Javascript - Debounce Function

About

A debounce is a function that:

For instance, in the context of:

debounce is said to come from the switch Contact bounce problem.

Usage

It's mostly used with event that triggers really often such as:

For example, in a search box that uses the change event, you don't want to search for every keystroke (ie to not search for every character of the search term)

Debounce function explained

This section will explain and comment a debounce function to show how it works.

A debounce is a javascript function that:

  • wraps and executes a callback function
  • when the debounce function itself has not been executed for a certain amount of time.

Basically, a call to the debounce function:

  • will schedule the target function to execute with the setTimeOut
  • delete the previous one if the debounce function was called again during the interval
  • let run the last schedule if the debounce function was not executed again during the interval

Function Signature

The signature of the debounce function]] has tree arguments:

  • funcToDebounce: the callback function to debounce
  • interval: the debounce interval in milliseconds where the debounce function should not be called to trigger the callback function execution.
  • leadingExecution: if true, execute the function:
    • before the interval (leading execution or immediate execution)
    • vs after (trailing execution)
function debounce(funcToDebounce, interval, leadingExecution) {

The timer Id

To execute after a certain amount of elpased time, the debounce function uses the setTimeOut function.

To keep track of the scheduled execution of the target function, we store the scheduler identifier (the returned value of the setTimeOut) in a timerId variable.

let timerId;

Every time that the debounce function is executed, this variable is not reset because we returns a function (it's called the closure functionality of javascript).

Returned function

Calling debounce returns a new anonymous function. Thanks to Javascript, this function is a closure and keeps track of its environment variables. The timerId will then persist and be available to this function.

return function() {

Scheduling condition

This condition checks if the function was already schedule.

It checks if timerId is a number (ie not null or undefined) and if this is the case it means that there is already a schedule running.

let wasFunctionScheduled = (typeof timerId === 'number');

Delete/Clear the previous schedule

The line below is the grand trick of the debounce function where:

  • we delete the previous scheduled execution
  • if the debounce function is called during the interval.

clearTimeout will delete the previous schedule function if timerId is valid/set or do nothing otherwise

clearTimeout(timerId);

The function to schedule

funcToSchedule is a wrapper function that:

Steps:

  • We first capture the execution parameters of the debounced function that we will pass to the execution function (apply)
let funcToDebounceThis= this, funcToDebounceArgs = arguments;
  • The funcToSchedule
let funcToSchedule = function() {

      // Reset
      clearTimeout(timerId);
      timerId = null;

      // trailing execution happens at the end of the interval
      if (!leadingExecution) {
        // Call the original function with apply
        funcToDebounce.apply(funcToDebounceThis, funcToDebounceArgs);
      }
      
}

Schedule the function to run at interval

Schedule each time an execution of the function over the interval.

timerId = setTimeout(funcToSchedule, interval);
  • If the debounce function is called again during this interval, it will be deleted with the previous clearTimeout
  • Otherwise, it will run

Leading Execution

If:

  • the callback function was not schedule before function entrance
  • the leading execution mode is on

Execute the function..

if (!wasFunctionScheduled && leadingExecution) funcToDebounce.apply(funcToDebounceThis, funcToDebounceArgs);

End of the returned function

}

End of debounce function

}

Demo: Mouse location

  • Printing the mouse location after a mousemove
function onMouseMove(e){
  console.log(new Date().toLocaleString()+": Position: x: "+e.x+ ", y:"+ e.y);
}
  • Debounce the onMouseMove function to not print for every mouve
let debouncedMouseMove = debounce(onMouseMove, 500);
  • Call the debounced function on every mouse move
window.addEventListener('mousemove', debouncedMouseMove);
  • Output: Move the mouse above the console below quickly without stopping, then stop, the last schedule event will then fire. ie when you:
    • move your mouse during the interval defined (500ms), all previous scheduling are deleted
    • stop moving the mouse for the interval defined (500ms), the last event will fire

Library

A debounce function is also generally available in utility library such as:

Debounce vs Throttle

  • A debounced function is called only once in a given period, delay milliseconds after its last invocation (the timer is reset on every call).
    • Debounce is great for keypress events; when the user starts typing and then pauses you submit all the key presses as a single event, thus cutting down on the handling invocations.
  • A throttled function is limited to be called no more than once every delay milliseconds.
    • Throttle is great for realtime endpoints that you only want to allow the user to invoke once per a set period of time.

Documentation / Reference


Powered by ComboStrap