javascriptvue.jsaxiosdebouncing

How can I use a debounce function for an axios call in Vue 3


I've got code that gets JSON data from a PHP script using axios. Currently this call is triggered on input of an input field.

This is fine except the call is triggered every key press. I would like to debounce this function for maybe half a second.

I've tried importing a function from another file that I created called debounce.js which contains:

const debounce = (fn, delay) => {
    let timeout
  
    return (...args) => {
      if (timeout) {
        clearTimeout(timeout)
      }
  
      timeout = setTimeout(() => {
        fn(...args)
      }, delay)
    }
  }
  
  export default debounce

I want to add the debounce to this method:

async fetchResults(){
    await axios.post('includes/searchproducts_json.php', {
        // Post data, add value of input to searchterm
        searchterm: this.$refs.searchterm.value
        
    })
    .then((response)=>{
        // Check if status is 200 (OK) and total_results is not 0
        // If true show results div and fill data
        if (response.status === 200 && response.data.total_results != 0) {
            this.showResultDiv = true
            this.results = response.data.results
            this.totalResults = response.data.total_results
            this.finishedCall = true
        }else{
            this.showResultDiv = false
        }
    })
    .catch((error)=>{
        console.log(error)
    })
    .then((response)=>{
        // Get length of returned object and add to results
        this.finishedCall = true
    })
}

I thought to wrap this debounce function around the entire method because I want to debounce the entire method, not just the retrieving data part. So I tried this:

setup(){
    async fetchResults = debounce(() => {
        await axios.post('includes/searchproducts_json.php', {
            // Post data, add value of input to searchterm
            searchterm: this.$refs.searchterm.value
            
        })
        .then((response)=>{
            // Check if status is 200 (OK) and total_results is not 0
            // If true show results div and fill data
            if (response.status === 200 && response.data.total_results != 0) {
                this.showResultDiv = true
                this.results = response.data.results
                this.totalResults = response.data.total_results
                this.finishedCall = true
            }else{
                this.showResultDiv = false
            }
        })
        .catch((error)=>{
            console.log(error)
        })
        .then((response)=>{
            // Get length of returned object and add to results
            this.finishedCall = true
        })

    }, 500)
    return { fetchResults }
}

But I get syntax errors like this. How can I correctly implement this debounce for the entire fetchResults method?

This is my entire JS code without the syntax errors:

import debounce from './assets/js/debounce.js'
let app = Vue.createApp({
    data: function(){
        return{
            // Object to fill with JSON response
            results:[],
            showResultDiv: false,
            totalResults: 0,
            finishedCall: false
        }
    },
    computed: {
        // Limit total results in searchdropdown to 6
        resultsToShow() {
            return this.results.slice(0, 6)
        },
        // Set the actual unlimited result amount to totalResults with matching string
        resultString(){
            if(this.totalResults == 1){
                return this.totalResults + ' resultaat'
            }else{
                return this.totalResults + ' resultaten'
            }
        }
    },
    methods:{
        // Function to show div with loading dots until json returned
        loadDiv(condition){
            this.showResultDiv = condition
        },
        async fetchResults(){
            await axios.post('includes/searchproducts_json.php', {
                // Post data, add value of input to searchterm
                searchterm: this.$refs.searchterm.value
                
            })
            .then((response)=>{
                // Check if status is 200 (OK) and total_results is not 0
                // If true show results div and fill data
                if (response.status === 200 && response.data.total_results != 0) {
                    this.showResultDiv = true
                    this.results = response.data.results
                    this.totalResults = response.data.total_results
                    this.finishedCall = true
                }else{
                    this.showResultDiv = false
                }
            })
            .catch((error)=>{
                console.log(error)
            })
            .then((response)=>{
                // Get length of returned object and add to results
                this.finishedCall = true
            })
        }
    }
})

app.mount('#v_search');

Solution

  • You can use debounce in Vue 3 like this if you don't want to use any additional packages.

    <input type="search" v-model="query" />
    
    const app = Vue.createApp({
      data() {
        return {
          query: "",
          results: [],
        };
      },
      methods: {
        debounce(func, timeout = 300) {
          let timer;
          return (...args) => {
            clearTimeout(timer);
            timer = setTimeout(() => {
              func.apply(this, args);
            }, timeout);
          };
        },
        fetchResults(query) {
          this.debounce(async () => {
            await axios
              .post("includes/searchproducts_json.php", {
                searchterm: query,
              })
              .then((response) => {
                if (response.status === 200 && response.data.total_results != 0) {
                  this.showResultDiv = true;
                  this.results = response.data.results;
                  this.totalResults = response.data.total_results;
                  this.finishedCall = true;
                } else {
                  this.showResultDiv = false;
                }
              })
              .catch((error) => {
                console.error(error);
              })
              .finally(() => {
                this.finishedCall = true;
              });
          }, 500);
        },
      },
      computed: {
        query: {
          get() {
            return this.query;
          },
          set(val) {
            this.fetchResults(val);
            this.query = val;
          },
        },
      },
    });