A debounced function call with JavaScript in a vue app
Tags: javascript, vue, webdev
Published: 2022-06-22 22:00:05

Here I want to talk about the concept of debounce, or rather something really similar to it. 

So I was faced with the need to limit the number of api calls I make to an endpoint, as the endpoint has a middleware that limits the amount of calls you can make to it. (a call per second for each client, for any api calls more than two per second, it throws an error). So if you also ever need to use this kind of api, this post will be helpful. 

The solution is rather simple thanks to the JavaScript setTimeout function. So based on the restriction set by this api, I needed a way to control the frequency of the api calls to be at most one per second.

Do note, I needed to call this api to implement a search functionality. This means that a user of the app will be calling the api per keystrokes (as they type to search). This resulted in the api getting called frequently as an average user type more than one character per second. 

The summary of solution to this is “for every api request a user make, check if there was any request made less than a second ago. If there is, “defer” the new api request to a second later, else call the api immediately”. 

The solution will be tailored to vuejs app but it can also be used on other stack. 

To implement this solution, the following will be needed.

```html
<input type="text" @input="handleSearch" placeholder="search"/>
```

```javascript
data() {
  return {
    can_start:true
    search_timeout:null
    can_start_timeout:null,
    delay:1000
}
```

```javascript
methods:{
  async search(){
    this.resetCanStart();
    await yourThrottledApi();
    // the api endpoint with request limit of 1 
    // request per second
  },
  
  deferredSearch() {
    if (this.search_timeout) clearTimeout(this.search_timeout);
    // this delete pending api request, so we don't have more 
    // than one pending request queued

    this.search_timeout = setTimeout(() => this.search(), 
    this.delay);
   // create a pending request which is called after a delay

  },

  handleSearch() {
     this.can_start ? this.search() : this.deferredSearch();
    // this handles the search such that search api request is 
    // called immediately if 'can_start' is true, else the
    // request is delayed for a second
    
  },

  resetCanStart() {
    this.can_start = false;

    if(this.can_start_timeout)
    clearTimeout(this.can_start_timeout);

    this.can_start_timeout = setTimeout(() => {
       this.can_start = true;
    }, this.delay);
   
    // this set 'can_start' to false immediately after a request, 
    // and then set it back to true after a delay (1 second) 
    // (i.e it blocks another request from being made by setting 
    // 'can_start' to false, then allows new request only after 1 
    //  second by setting 'can_start' to true
}


}

```

Implications of the solution 

1. For instance where the api returns the result in less than a second (say T second), consequent api calls made immediately will be forcibly delayed for some moment i.e (1 second - T second).

2. Some api request won't be made if both of these conditions are true
  *  the current request is made less then a second after the last request
  *  another request is made less than a second after the current request