Web performance is a key aspect and often discussed interview topic. All companies want their websites to load faster, stay performant, and provide smooth user experience.
Debouncing is one of the techniques used to improve performance by limiting the frequency of a particular function being called, particularly in scenarios where the function is triggered frequently, such as input events like typing in an input field or scrolling. This ensures that the function is only called once after a specified period of inactivity.
Implementation
We can implement Debounce by creating a Higher Order function that will take the original function and a delay amount as input and returns a debounced version.
function debounce(fn, delay) {
return function executedFunction() {
// some processing
}
}
The main behaviour of the debounce function is to defer the execution of the original function by the delay provided as the argument. We can do so by using the setTimeout
method.
function debounce(fn, delay) {
let timer;
return function executedFunction() {
timer = setTimeout(function() {
fn();
}, delay);
}
}
The idea is to call the original fn
function if there is no user action for delay
milliseconds. On every user action, the executedFunction
would be called every timing. As per our current implementation, we are going to set a new timer that would result in n
API calls. However, this is not the desired behaviour, we need to clear the old timer and set a timer on every user action.
function debounce(fn, delay) {
let timer;
return function executedFunction() {
// clearing the current timer
clearTimeout(timer);
// settings a new timer
timer = setTimeout(function() {
fn();
}, delay);
}
}
Now, we need to pass the arguments provided by the user to the original fn
function.
function debounce(fn, delay) {
let timer;
return function executedFunction() {
// arguments is an array-like object accessible inside a function
// it contains all the input arguments provided to the user
// we are converting it to an actual array using slice
const args = Array.prototype.slice.call(arguments);
// storing calling reference
const context = this;
// clearing the current timer
clearTimeout(timer);
// settings a new timer
timer = setTimeout(function() {
// invoking the fn with current context (this) and passing args
// apply method changes the context and takes arguments as an array
fn.apply(context, args);
}, delay);
}
}
This satisfies are all requirements. We are returning a debounced version of the original function that is invoked after every n
milliseconds.
Examples
One example of debouncing is in handling search input on a website. Imagine a scenario where a user is typing in a search box to filter results. Without debouncing, a search API call would be triggered on every keystroke, leading to multiple unnecessary API calls. By implementing debouncing, we can delay the API call until the user stops typing for a specified amount of time, therefore reducing the number of API requests made and improving the overall performance of the search feature.
function search(query) {
...
// make API call here
}
const debouncedSearch = debounce(search, 300);
// debouncedSearch method either inside useEffect or onChange
Practice
This concept is asked an interview questions of multiple companies like Flipkart, Visa, Amazon, and many more. You can try and see if you can pass all the test cases using the link below.