One of the most important components of modern online apps is performance. As developers, we employ a variety of ways to improve application speed and user experience. Debouncing and throttling, for example, are two basic yet effective approaches for improving speed in JavaScript applications.


In this blog, we will be understanding debounce and throttling in Javascript. 


Why do we need to debounce and throttle?


Usually, developers are able to choose when to call a function. However, they must sometimes transfer control to users. Event triggers, for example, are commonly used in applications to trigger functions depending on events such as key presses, button clicks, and mouse movements. In such cases, users can trigger those events considerably more frequently than necessary, causing substantial performance concerns in the application.


For example, consider a search bar where we need to display a suggestion list as the user types. We can implement this using an event listener to bring data from the backend on each key press. However, it will negatively impact application performance, since it will make an API call each time the user presses a key.


The following code shows an example of this scenario. It will make 10 API calls if we type the word JavaScript.


<html>

  <body>

    <label>Search Here : </label>

    <input type="text" id="search-bar" />


    <script>

      var searchBarDom = document.getElementById('search-bar');

      var numberOfKeyPresses = 0;

      var numberOfApiCalls = 0;


      function getSearchResult() {

        numberOfApiCalls += 1;

        console.log('Number of API Calls : ' + numberOfApiCalls);

      }


      searchBarDom.addEventListener('input', function (e) {

        numberOfKeyPresses += 1;

        console.log('Search Keyword : ' + e.target.value);

        console.log('Number of Key Presses : ' + numberOfKeyPresses );

        getSearchResult();

      });

    </script>

  </body>

</html>


Output:




As you can see, there is a significant number of unnecessary API calls in the previous example. In such situations, we can use debouncing and throttling to reduce the number of API calls triggered by the user event and improve the application performance without any drag on the user experience.


So, let’s see these two techniques and how we can implement them.


 What is debounce?


The concept of debouncing is pretty straightforward. It delays the function invocation by a defined period of time to avoid unnecessary invocations. So, the function will only be invoked if no event is triggered within that time. If the user triggers a new event during that time, the time will be reset.


Let’s consider the previous example again. The issue with the scenario was unnecessary API calls. So, if we define a debounce function with a delay of one second, it will hold back the API call until one second passes without any user events. If the user presses a key within that second, the debounce function will reset the delay and wait for another second to make the API call.


Implement a debounce function


We can easily implement a debounce function using the setTimeout() and clearTimeout() functions. It should also take a callback function and the delay as input parameters.


function debounce(callback, delay = 1000) {

  var time;


  return (...args) => {

    clearTimeout(time);

    time = setTimeout(() => {

      callback(...args);

    }, delay);

  };

}


As you can see, the debounce function acts as a wrapper for the callback function and ensures that it will be invoked after the delay. In this case, the default delay is 1,000 milliseconds.


Let’s now modify the search bar example code with a debounce function.


<html>

  <body>

    <label>Search Here : </label>

    <input type="text" id="search-bar" />


    <script>

      var searchBarDom = document.getElementById('search-bar');

      var numberOfKeyPresses = 0;

      var numberOfApiCalls = 0;


      const getSearchResult = debounce(() => {

        numberOfApiCalls += 1;

        console.log('Number of API Calls : ' + numberOfApiCalls);

      }, 1000);


      searchBarDom.addEventListener('input', function (e) {

        numberOfKeyPresses += 1;

        console.log('Search Keyword : ' + e.target.value);

        console.log('Number of Key Presses : ' + numberOfKeyPresses);

        getSearchResult();

      });


      function debounce(callback, delay = 1000) {

        var time;

        return (...args) => {

          clearTimeout(time);

          time = setTimeout(() => {

            callback(...args);

          }, delay);

        };

      }

    </script>

  </body>

</html>


Output:




Although the user has typed the word thecodingev, only a single API call has been invoked. So, debouncing has blocked tewleve unnecessary API calls from the previous example.



What is throttle?


Throttle is another technique to minimize unnecessary function invocations when using event listeners. However, throttle works a bit differently from debouncing. Instead of delaying, it invokes the callback function at regular intervals as long as the event trigger is active.


For example, assume we have defined the delay as one second in a throttle function. First, it will invoke the callback function immediately. Then, it will use the delay time as the waiting time and invoke the callback function every second until the event trigger becomes inactive.


Implement a throttle function


We can implement a throttle function using the setTimeout() function and a flag variable. It should take a callback function and the delay as input parameters.


function throttle(callback, delay = 1000) {

  let shouldWait = false;


  return (...args) => {

    if (shouldWait) return;


    callback(...args);

    shouldWait = true;

    setTimeout(() => {

      shouldWait = false;

    }, delay);

  };

}


In debouncing, we implemented the debounce function as a wrapper of the callback function, since we delay the callback function invocation. But in throttle implementation, we immediately invoke the callback function if the shouldWait flag is false. Then, the setTimeout() function is used to update the flag value based on the delay we define.


The following code shows the initial search bar example optimized with a throttle function.


<html>

  <body>

    <label>Search Here : </label>

    <input type="text" id="search-bar" />


    <script>

      var searchBarDom = document.getElementById('search-bar');

      var numberOfKeyPresses = 0;

      var numberOfApiCalls = 0;


      const getSearchResult = throttle(() => {

        numberOfApiCalls += 1;

        console.log('Number of API Calls : ' + numberOfApiCalls);

      }, 1000);


      searchBarDom.addEventListener('input', function (e) {

        numberOfKeyPresses += 1;

        console.log('Search Keyword : ' + e.target.value);

        console.log('Number of Key Presses : ' + numberOfKeyPresses);

        getSearchResult();

      });



      function throttle(callback, delay = 1000) {

        let shouldWait = false;


        return (...args) => {

          if (shouldWait) return;


          callback(...args);

          shouldWait = true;

          setTimeout(() => {

            shouldWait = false;

          }, delay);

        };

      }

    </script>

  </body>

</html>


Output:



As you can see, only three API calls were made in this example when I typed the word thecodingdev. The first call is the initial call, and the other two were made after 2 and 9 key presses, respectively. So, the throttle function has reduced seven unnecessary API calls.


Difference between debounce and throttle


Debounce monitors the time delay between user actions and only executes the callback function if the delay exceeds the time delay defined by the developer. So, continuous user actions can significantly delay the callback function’s execution if we use debounce.


On the other hand, throttle uses the time delay to execute the callback function at regular intervals until the event trigger is active. So, it does not delay the callback function execution for a significant period like debounce.


Conclusion


This article discussed how we could use debounce and throttle to handle user events and minimize unnecessary function executions in JavaScript applications. Ultimately, these two techniques can significantly improve your application’s performance since they can avoid many unnecessary API calls, DB queries, and so on. So, give them a try when implementing event triggers on your next application.


If you have any doubt, let us know in comments.