Implementing a debounce function is one of the most common questions for any JavaScript developer in interviews. There are numerous blogs and tutorials where people have explained the concept with images and simplicity, but this article will aim to give you a deeper understanding.
Concept Refresher 🤷♂️
What is Debouncing ?
If you have ever pressed a lift button you have used the debouncing concept.
Imagine you’re waiting for a lift, and in frustration, you press the lift button multiple times in quick succession. What happens? Ideally, the lift system should recognize this bombardment of inputs as a single request and not try to send multiple lifts to your floor. 🚪 This is the essence of debouncing. The system "waits" ⏳ until the user stops pressing the button before processing the request. By ignoring intermediate presses and acting only on the last one, debouncing ensures efficiency and prevents redundant operations. 🔄
Principle:- ignore the noise and focus on the final action.
Definition :- If a function is invoked multiple times in quick succession, the debounced function should execute only the last invocation, provided no further calls are made within a specified time limit.
Code for lift button logic
// Simulate an API call or ride request
function requestLyftRide() {
console.log("Lyft ride requested...");
}
// Debounced version of the function
const debouncedRequestLyftRide = debounce(requestLyftRide, 2000); // 2-second debounce
// Grab the Lyft button from the DOM
const lyftButton = document.getElementById('lyftButton');
// Attach debounced function to the button click event
lyftButton.addEventListener('click', () => {
console.log("Button clicked");
debouncedRequestLyftRide(); // Call the debounced version of the function
});
🔥Technical requirements for implimentation🔥
Function Creation: Write a function that returns a debounced version of the original function.
Execution Delay: Execution is delayed by
t
milliseconds and is canceled if called again within that window.Parameter Passing: The debounced function should receive the passed parameters.
Context Preservation: The debounced function should preserve the context of the function.
First implimentation
/**
* @param {Function} fn
* @param {number} t milliseconds
* @return {Function}
*/
var debounce = function(fn, t) {
let timer = null; // encapsulated timer variable
return function(...args) {
clearTimeout(timer); //clear existing timer
timer = setTimeout(()=>fn(...args),t); //set new timer with call back
}
};
This code is sufficient to pass leetcode problem 2627. Debounce but this implementation can cause a potential issue ie the loss of context.
Issue with first implementation.
The 4th technical requirement to preserve context is not implemented because of the use of arrow function.
Below is a simple scenario where the code behavior is unpredictable
const obj = {
value: 42,
debouncedLog: debounce(function () {
console.log(this.value);
}, 1000),
};
obj.debouncedLog();
// After 1 second, `this` inside the debounced function would not refer to `obj`,
// so `undefined` or an error would occur depending on the environment.
Updated Implementation.
The problem can be fixed by passing anonymous function as call back and bind the context to preserve the context.
/**
* @param {(...args: any[]) => any} func
* @param {number} wait
* @returns {(...args: any[]) => any}
*/
function debounce(func, wait) {
// Initialise encapsulated timer variable
let timer = null;
return function (...args) {
// Preserve the context of `this`
const context = this;
// Clear any existing timer
clearTimeout(timer);
// Set a new timer
timer = setTimeout(function () {
// Ensure the function is called with the correct context and arguments
func.apply(context, args);
}, wait);
};
}
Summary
Debouncing is a technique used to ensure that a function is executed only once after a series of rapid calls, similar to how a lift button should only register a single press despite multiple quick presses.
The principle of debouncing is to "ignore the noise and focus on the final action."
A debounced function executes only the last invocation if no further calls are made within a specified time limit.
Technical requirements for implementing a debounce function include function creation, execution delay, parameter passing, and context preservation.
The initial implementation of a debounce function may cause a loss of context due to the use of arrow functions.
The issue of context loss can be resolved by using an anonymous function and binding the context.
Testing the knowledge
If you really understood the concept and need to test how good your understanding is
try this problem
https://bigfrontend.dev/problem/implement-debounce-with-leading-and-trailing-option
Test file for Debounce code.
Testing code can be collected from here.The tests are written in jest covering corner scenarios.
https://github.com/arsalanadeeb/javascript/blob/main/closure/debounce/debounce.spec.js
connect with me here https://www.linkedin.com/in/arsalan-adeeb/