Debounce

Updated on 16 May, 2025
Debounce header image

Problem Statement

The task is to create a debounced version of a function. A debounced function is designed to increase the efficiency of handling frequently fired events by delaying the execution until a certain period (t milliseconds) has lapsed. This is particularly useful in scenarios where a function should only proceed after a pause in activity. The effective result is that the function execution is postponed by t milliseconds each time and is cancelled if a new call arrives within that postponement period.

For a precise scenario, if t = 50ms, and function is triggered at timesteps 30ms, 60ms, and 100ms, only the function call at 100ms would proceed at 150ms. This happens because the earlier calls at 30ms and 60ms are overridden by the subsequent calls that happen within the 50ms period from their respective initiation times.

Examples

Example 1

Input:

t = 50
calls = [
  {"t": 50, inputs: [1]},
  {"t": 75, inputs: [2]}
]

Output:

[{"t": 125, inputs: [2]}]

Explanation:

let start = Date.now();
function log(...inputs) {
  console.log([Date.now() - start, inputs ])
}
const dlog = debounce(log, 50);
setTimeout(() => dlog(1), 50);
setTimeout(() => dlog(2), 75);
The 1st call is cancelled by the 2nd call because the 2nd call occurred before 100ms
The 2nd call is delayed by 50ms and executed at 125ms. The inputs were (2).

Example 2

Input:

t = 20
calls = [
  {"t": 50, inputs: [1]},
  {"t": 100, inputs: [2]}
]

Output:

[{"t": 70, inputs: [1]}, {"t": 120, inputs: [2]}]

Explanation:

The 1st call is delayed until 70ms. The inputs were (1).
The 2nd call is delayed until 120ms. The inputs were (2).

Example 3

Input:

t = 150
calls = [
  {"t": 50, inputs: [1, 2]},
  {"t": 300, inputs: [3, 4]},
  {"t": 300, inputs: [5, 6]}
]

Output:

[{"t": 200, inputs: [1,2]}, {"t": 450, inputs: [5, 6]}]

Explanation:

The 1st call is delayed by 150ms and ran at 200ms. The inputs were (1, 2).
The 2nd call is cancelled by the 3rd call
The 3rd call is delayed by 150ms and ran at 450ms. The inputs were (5, 6).

Constraints

  • 0 <= t <= 1000
  • 1 <= calls.length <= 10
  • 0 <= calls[i].t <= 1000
  • 0 <= calls[i].inputs.length <= 10

Approach and Intuition

  1. Initial Setup:

    • Define a wrapper function that incorporates timing logic to delay function execution based on the most recent call and t.
    • This function will keep track of the last call timing using a timeout.
  2. Handling Calls:

    • Each time the debounced function is invoked, cancel the pending execution if any (using the timeout from the previous call), and schedule a new one.
    • This is accomplished with setTimeout, setting it for t milliseconds into the future.
  3. Execution Conditions:

    • If no new calls are made within t milliseconds from the last call, the function is executed with the parameters of the last issued call.
    • If a new call comes in during this window, reset the timer with the new call's parameters.
  4. Example Walkthroughs:

    • Example 1: t = 50ms
      • A call at 50ms with input [1] schedules an execution at 100ms.
      • Before this can occur, another call at 75ms with [2] comes in. The previous timer is cleared, and a new execution for [2] is scheduled at 125ms.
      • Since no further calls occur before 125ms, [2] is executed at this time.
    • Example 2: t = 20ms
      • A function call at 50ms results in execution at 70ms.
      • Another call at 100ms results in a separate execution at 120ms.
    • Example 3: t = 150ms
      • A function call at 50ms with inputs [1, 2] results in an execution at 200ms.
      • A call at 300ms is superseded by another call at the same time (300ms) but with different inputs [5, 6].
      • The call with inputs [5, 6] eventually gets executed at 450ms.

This systematic postponement ensures that functions are not called too frequently in quick succession, potentially saving computation and enhancing performance especially in resource-intensive or I/O-bound operations.

Solutions

  • JavaScript
js
var debounceFunction = function(func, delay) {
  let timer;
  return function(...arguments) {
    const previous = Date.now();
    clearInterval(timer);
    timer = setInterval(() => {
      if (Date.now() - previous >= delay) {
        func(...arguments);
        clearInterval(timer);
      }
    }, 1);
  }
};

The code snippet provides a JavaScript implementation of the debounce technique. This pattern is especially useful in optimizing performance by limiting the rate at which a function can fire. For instance, it can be applied to events like scrolling, resizing, or keypresses in web development to reduce the number of event calls.

Here's a breakdown of how the provided debounce function works:

  • It accepts two parameters:

    • func: The function you want to debounce.
    • delay: The minimum time interval (in milliseconds) that should elapse before the function is allowed to execute again.
  • Inside, a timer variable is declared using let, ensuring it's only accessible within the debounceFunction's scope.

  • The returned function captures any arguments passed to it using the JavaScript rest parameter syntax ...arguments. This allows the function to accept and pass on an indefinite number of arguments to func.

  • Upon invoking the returned function, it clears any previous timeout set by timer to ensure it starts fresh.

  • A new interval is set with setInterval. Inside its callback:

    • It checks if the current time minus the time of the previous call (previous) is greater than or equal to the specified delay.
    • If true, it executes the function func with the originally provided arguments and clears the interval to prevent further execution.

This implementation ensures that func is not executed more frequently than the provided delay. This pattern is beneficial for enhancing performance and reducing resource consumption in scenarios where functions might be called frequently and rapidly.

Comments

No comments yet.