Search code examples
lodashvue.js

How to debounce an AJAX call with VueJS?


I am trying to debounce an AJAX call to only send it once, but unfortunately, it tells me that what I am passing it is not a function. Here is my code:

getItems: function(page){
  this.page = page;
  console.log(typeof(this.getBackendItems));
  _.debounce(this.getBackendItems(page), 500);
},
getBackendItems: function(page){
  this.$http.get("{{url('api/user/items')}}", {page : page}).success(function(response){
      this.items = response.data;
      this.last_page = response.last_page;
  });
},

Console.log says that getBackendItems is a function and still an error is thrown that not a function is passed to _.debounce() call.


Solution

  • There are several things wrong with this:

    Passing the function correctly

    When you are trying to pass the function like this: this.getBackendItems(page) you are running it, so _.debounce does not receive the function, but the result of the function, which in this case, is a Promise object.

    • this.getBackendItems passes the function
    • this.getBackendItems(page) runs the function, and passed the result

    But then, how do you tell it which arguments to use?

    Correctly calling a debounced Function

    Well, _.debounce() returns your original function, but wrapped with the debounce logic. So you cache it in a variable and execute that function with your desired arguments (page). So the syntactically correct Way to use debounce would be more like this:

    getItems: function(page){
      this.page = page;
      console.log(typeof(this.getBackendItems));
      var debouncedFunction = _.debounce(this.getBackendItems.bind(this), 500); // properly bind the function so "this" is the vue component.
      debouncedFunction(page)
    },
    

    Debouncing the right function

    But, that too won't work, because getItems itself is not debounced, so all this code will do is create many, many debounced functions, which will all fire once the 500ms is kicking in. Not a good idea.

    Now my question is: how do you invoke this function? from a v-on:click? Then Vue got you covered:

    Using Vue's debounce filter

    <a v-on:click="getItems(page) | debounce 500">Some Link</a>
    

    With that filter, you don't need to use _.debounce at all.

    If you call this function some other way, let me know.

    Edit: update due to comment feedback:

    debounce function while counting the number of debounces

    <a href="#"
      @click.prevent="getItems(page) | debounce 500" 
      @click="pageBuffer = pageBuffer +1">
      Click Me
    </a>
    

    JS:

    var App = new Vue({
      el: '#app',
      data() {
        return {
                page: 1,
          pageBuffer: 0
        }
      },
      methods: {
        getItems: function(page) {
          this.getBackendItems(page + this.pageBuffer)
          this.pageBuffer = 0
        }
      }
    })