Debouncing or Throttling

2022.11.11

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

ISSUE

In the user interactive applications, there are actions and triggers established on user clicks, presses, and mouse movements. Every time one of those events occurs, a function runs, and this can happen more frequently than necessary, impacting the performance.

Imagine a search bar with type-ahead functionality that requires us to fetch the results from an API. On the four-letter words, four API requests are made, but only the final one is vital. To handle such situations we have two mechanisms called Debouncing and Throttling.

DEBOUNCING

Debouncing: regardless of how many times the event is fired by the user, the function won't be called until a certain amount of time has passed since the last fire.

Let us understand this concept with a simple example of an autocomplete component for web apps. as the user writes each letter, we would have to execute a function to query an API to collect potential possibilities. The problem with doing it keypress by keypress is that there will be numerous calls and, to make matters worse, you won't care about all of them; just the final one will be important. Therefore, the goal is to debounce the function's call. You can call it as many as you like, but no actual API calls will be made until after a certain amount of time has passed with no new calls.

This technique will work something like “whenever you stop, I hit”. Recently, people’s typing skills are quite fast. So, this technique’s timeout relies on the speed of the user's typing. For the above example, we shall trigger an event to execute after a delay of 0.2~0.3 seconds.

DEBOUNCE HELPER FUNCTION:

index.js

// helper Function
const debounce = (func, delay = 200) => {
 let timeoutId;

 return function () {
   clearTimeout(timeoutId);
   timeoutId = setTimeout(() => {
     func.apply(this, arguments);
   }, delay);
 };
};

function search(evt) {
 if(evt.target === this){
   console.log("function is called")
 }
}

// using debounce helper function
const debouncedSearch = debounce(search, 300);
const input = document.querySelector('#search');
input.addEventListener('input', debouncedSearch);

index.html

<!DOCTYPE html>
<html lang="en">
 <head>
   <meta charset="UTF-8" />
   <meta http-equiv="X-UA-Compatible" content="IE=edge" />
   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
   <title>Document</title>
 </head>
 <body>
   <div>
     <input type="text" id="search" />
   </div>
   <script src="./index.js"></script>
 </body>
</html>

DEBOUNCE OUTPUT

Despite more than 10 characters being typed, we see the function being called just twice, which apparently was after a pause in typing and also with a wait of 0.3seconds.

THROTTLING:

Throttling: regardless of how many times the event is fired by the user, the function executes only once in the specified interval.

As an example consider a form where a user enters an id, and when they click the “retrieve” button, an API call is sent to a server to retrieve data that matches that id. Numerous calls will be placed if the user repeatedly clicks the button, but they will certainly end up with the same outcomes all the time. Here we might want to throttle the calls to the API so that the first call will go through but that subsequent calls won't be made for a certain period of time. Similar issues could arise in video games: what if a player keeps clicking the "shoot" button even though there is a cooling period in between shots?

Throttling would be helpful in circumstances like the above where we are aware that the user might hit a button inadvertently. All you need to do is allow the event to run once, and then for x milliseconds, stop it from happening again.

THROTTLING HELPER FUNCTION:

index.js

// helper Function
let timerId,
 clicked = 0,
 called = 0;
let throttleFunction = function (func, delay) {
 if (timerId) {
   return;
 }
 timerId = setTimeout(function () {
   func();
   timerId = undefined;
 }, delay);
};

const funcCalled = document.getElementById('output-called');

function clickFun() {
 called += 1;
 funcCalled.innerHTML = 'Number of function Calls: ' + called;
 console.log('yes it is called');
}

const input = document.getElementById('submit');
const clickedOutput = document.getElementById('output-click');

input.addEventListener('click', () => {
 clicked += 1;
 clickedOutput.innerHTML = 'Number of Button Clicks: ' + clicked;
 throttleFunction(clickFun, 500);
});

index.html

<!DOCTYPE html>
<html>
 <head>
   <title>Title of the document</title>
 </head>

 <body>
   <div>
     <button id="submit">click me</button>
     <p id="output-click"></p>
     <p id="output-called"></p>
   </div>
   <script type="text/javascript" src="./index.js"></script>
 </body>
</html>

THROTTLING OUTPUT

Despite any number of clicks, we see the function being called limitedly at regular time intervals.

CONCLUSION

Debounce and throttle are ideal when dealing with events associated with users interactions that cause API calls. They save you money on server costs, data costs, and improve the overall performance of your app.

Rather than implementing the helper functions, we can also use the build-in libraries in npm here.

Thank you.