SoFunction
Updated on 2025-04-12

Detailed explanation of the implementation example of vue blocking duplicate requests

background

In the project, the front-end code will encounter the same request being sent to the server multiple times. We must avoid wasting server resources. Only once requests are allowed for a certain period of time.

Ideas

(1) If the business is simple, for example, the same button prevents multiple clicks, we can use the timer to prevent shadowing.

(2) If the business is complex, for example, multiple components pass through code and the same request is sent multiple times, the anti-shake is no longer easy to deal with. It is best to cancel the repeated ajax requests in a unified manner.

Method 1 - Anti-shake treatment through timer

(a) Overview

Effect: When the user clicks the same button multiple times in a row, after the last click, a request will be initiated after a short period of time.
principle: After each call of the method, a timer is generated. After the timer is finished, the request is sent. If the method is called repeatedly, the current timer will be cancelled, a new timer will be created, and the request will be sent after the end. During work, the tool functions that can be encapsulated by third-party tools, such aslodashofdebounceMethods to simplify the code of anti-shake

(b) Code

<!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>
    <script src="/cdn/expire-1-M//4.17.21/" type="application/javascript"></script>
    <script src="/cdn/expire-1-M/vue/2.6.14/" type="application/javascript"></script>
    <script src="/cdn/expire-1-M/axios/0.26.0/" type="application/javascript"></script>
</head>
<body>
    <div >
        <button @click="onClick">ask</button>
    </div>
</body>
<script>
// Define the request interfacefunction sendPost(data){
    return axios({
        url: '-cloud-studio-demo./test',
        method: 'post',
        data
    })
}
new Vue({
    el: '#app',
    methods: {
        // Call lodash's anti-shake method debounce, realizes that the button is clicked several times in a row, and the interface is called once after 0.3 seconds        onClick: _.debounce(async function(){
            let res = await sendPost({username:'zs', age: 20})
            ('Result of the request', )
        }, 300),
    },
})
</script>
</html>

(c)Preview

connect

(d) Existing problems

The problem of sending repeated requests for multiple button pieces cannot be solved, such as the following two situations

Situation - Anti-shake on click events

Button events are independent of each other, and different methods are called, so the anti-shake effect between buttons cannot be achieved.

<!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>
    <script src="/cdn/expire-1-M//4.17.21/" type="application/javascript"></script>
    <script src="/cdn/expire-1-M/vue/2.6.14/" type="application/javascript"></script>
    <script src="/cdn/expire-1-M/axios/0.26.0/" type="application/javascript"></script>
</head>
<body>
    <div >
        <button @click="onClick1" ref="btn1">ask1</button>
        <button @click="onClick2" ref="btn2">ask2</button>
    </div>
</body>
<script>
let sendPost = function(data){
    return axios({
        url: '-cloud-studio-demo./test',
        method: 'post',
        data
    })
}
new Vue({
    el: '#app',
    mounted() {
        this.$refs.()
        this.$refs.()
    },
    methods: {
        // Use lodash to prevent the request method        //There is a problem here. It is just that the click event of each button is used separately, but the effect of anti-shake cannot be achieved between the two buttons.        onClick1: _.debounce(async function(){
            let res = await sendPost({username:'zs', age: 20})
            ('Result of request 1', )
        }, 300),
        onClick2: _.debounce(async function(){
            let res = await sendPost({username:'zs', age: 20})
            ('Result of Request 2', )
        }, 300),
    },
})
</script>
</html>

Preview

Situation - Anti-shake in the interface method

The methods called between buttons are the same, and the method can be anti-shake processing, but the processing itself encapsulates the method once, which will affect the reception of the return value of the previous method. More processing is required for the previous method, which becomes more complicated.Not recommended

<!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>
    <script src="/cdn/expire-1-M//4.17.21/" type="application/javascript"></script>
    <script src="/cdn/expire-1-M/vue/2.6.14/" type="application/javascript"></script>
    <script src="/cdn/expire-1-M/axios/0.26.0/" type="application/javascript"></script>
</head>
<body>
    <div >
        <button @click="onClick1" ref="btn1">ask1</button>
        <button @click="onClick2" ref="btn2">ask2</button>
    </div>
</body>
<script>
// Use lodash to prevent the request method,let sendPost = _.debounce(function(data){
    //There is a problem here. The return value here cannot be used as the return value executed by the sendPost method, because the debounce is wrapped inside it with a layer    return axios({
        url: '-cloud-studio-demo./test',
        method: 'post',
        data
    })
}, 300)
new Vue({
    el: '#app',
    mounted() {
        this.$refs.()
        this.$refs.()
    },
    methods: {
        onClick1: async function(){
            //There is a problem here. The return value of sendPost is not promise, but undefined            let res = await sendPost({username:'zs', age: 20})
            ('Result of request 1', res)
        },
        onClick2: async function(){
            let res = await sendPost({username:'zs', age: 20})
            ('Result of Request 2', res)
        },
    },
})
</script>
</html>

Preview

Way 2 - By canceling ajax request

(a) Overview

Directly process the request method, and cancel the duplicate request through the ajax library API method

(b) Principle

Native ajax cancel request

By callingXMLHttpRequestObject instanceabortMethod cancel the request

<!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>
</body>
<script>
//Native Ajax syntaxlet xhr = new XMLHttpRequest();
("GET", "-cloud-studio-demo./test?username=zs&age=20", true);
 = function(){
    ()
}
();
//Test under low speed 3g in Google Chrome//Cancel the request through the abort method of the XMLHttpRequest instancesetTimeout(() => (), 100);
</script>
</html>

Preview

axios cancel request

passaxiosofCancelTokenObject instancecancelMethod cancel the request

<!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>
    <script src="/cdn/expire-1-M/axios/0.26.0/" type="application/javascript"></script>
</head>
<body>
</body>
<script>
/*Axios cancellation syntax*/
// Method 1 - By generating cancelToken and cancel methods/*
 const source = ();
 ('-cloud-studio-demo./test', {
     params: {username: 'zs', age: 20},
     cancelToken:
 }).then(res=>{
     ('res', )
 }).catch(err=>{
     ('err', err)
 })
 //Test under low speed 3g in Google Chrome
 //Cancel by calling the cancel method of source
 setTimeout(() => (), 100);
 */
/**/
// Method 2 - Generate cancelToken and cancel methods through newlet cancelFn 
const cancelToken =  new (cancel=>{
    cancelFn = cancel
});
('-cloud-studio-demo./test', {
    params: {username: 'zs', age: 20}, 
    cancelToken: cancelToken
}).then(res=>{
    ('res', )
}).catch(err=>{
    ('err', err)
})
//Test under low speed 3g in Google Chrome//Cancel by calling cancelFn methodsetTimeout(() => cancelFn(), 100);
</script>
</html>

Preview

(c) Code

Step 1 - Cancel duplicate request via axios request interceptor

passaxiosThe request interceptor puts the request information and the request cancel method into a map object before each request, and determines whether the request information already exists in the map object. If there is a cancel upload request

<!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>
    <script src="/cdn/expire-1-M/vue/2.6.14/" type="application/javascript"></script>
    <script src="/cdn/expire-1-M/axios/0.26.0/" type="application/javascript"></script>
    <script src="/cdn/expire-1-M/qs/6.10.3/" type="application/javascript"></script>
</head>
<body>
    <div >
        <button @click="onClick1" ref="btn1">ask1</button>
        <button @click="onClick2" ref="btn2">ask2</button>
    </div>
</body>
<script>
// Map object that stores request information and cancel methodconst pendingRequest = new Map();  
//Create the map key based on the requested information (request method, url, request get/post data).function getRequestKey(config){
    const { method, url, params, data } = config;
    return [method, url, (params), (data)].join("&");
}   
//Request an interceptor(
    function (config) {
    //Create the map key based on the requested information (request method, url, request get/post data).    let requestKey = getRequestKey(config)
    //Discern whether the request is repeated    if((requestKey)){
        //Cancel the last request        let cancel = (requestKey)
        cancel()
        //Delete the request information        (requestKey) 
    }
    //Add request information to map    // Generate cancel method     =  || new (cancel => {
        // Add the cancel method to map        if (!(requestKey)) {
            (requestKey, cancel)
        }
    })
    return config;
  },
  (error) => {
     return (error);
  }
);
let sendPost = function(data){
    return axios({
        url: '-cloud-studio-demo./test',
        method: 'post',
        data
    })
}
new Vue({
    el: '#app',
    mounted() {
        this.$refs.()
        this.$refs.()
    },
    methods: {
        // Use lodash to prevent the request method        //There is a problem here. It is just that the click event of each button is used separately, but the effect of anti-shake cannot be achieved between the two buttons.        onClick1: async function(){
            let res = await sendPost({username:'zs', age: 20})
            ('Result of request 1', )
        },
        onClick2: async function(){
            let res = await sendPost({username:'zs', age: 20})
            ('Result of Request 2', )
        },
    },
})
</script>
</html>

Preview

Step 2 - Response interceptor to successfully handle the request

passaxiosThe response interceptor deletes the data of the request information in the map object after the request is successful.

<!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>
    <script src="/cdn/expire-1-M/vue/2.6.14/" type="application/javascript"></script>
    <script src="/cdn/expire-1-M/axios/0.26.0/" type="application/javascript"></script>
    <script src="/cdn/expire-1-M/qs/6.10.3/" type="application/javascript"></script>
</head>
<body>
    <div >
        <button @click="onClick1" ref="btn1">ask1</button>
        <button @click="onClick2" ref="btn2">ask2</button>
    </div>
</body>
<script>
// Map object that stores request information and cancel methodconst pendingRequest = new Map();  
//Create the map key based on the requested information (request method, url, request get/post data).function getRequestKey(config){
    const { method, url, params, data } = config;
    return [method, url, (params), (data)].join("&");
}   
//Request an interceptor(
    function (config) {
    //Create the map key based on the requested information (request method, url, request get/post data).    let requestKey = getRequestKey(config)
    //Discern whether the request is repeated    if((requestKey)){
        //Cancel the last request        let cancel = (requestKey)
        cancel()
        //Delete the request information        (requestKey) 
    }
    //Add request information to map    // Generate cancel method     =  || new (cancel => {
        // Add the cancel method to map        if (!(requestKey)) {
            (requestKey, cancel)
        }
    })
    return config;
  },
  (error) => {
     return (error);
  }
);
//Response Interceptor(
  (response) => {
        //Request succeeds        //Delete the requested information        let requestKey = getRequestKey()
        if((requestKey)){
            (requestKey)   
        }
        return response;
   },
   (error) => {
        return (error);
   }
);
let sendPost = function(data){
    return axios({
        url: '-cloud-studio-demo./test',
        method: 'post',
        data
    })
}
new Vue({
    el: '#app',
    mounted() {
        this.$refs.()
        this.$refs.()
    },
    methods: {
        // Use lodash to prevent the request method        //There is a problem here. It is just that the click event of each button is used separately, but the effect of anti-shake cannot be achieved between the two buttons.        onClick1: async function(){
            let res = await sendPost({username:'zs', age: 20})
            ('Result of request 1', )
        },
        onClick2: async function(){
            let res = await sendPost({username:'zs', age: 20})
            ('Result of Request 2', )
        },
    },
})
</script>
</html>

Preview

Step 3 - Failed to handle request via axios response interceptor

passaxiosThe response interceptor deletes the data of the request information in the map object after the request fails.

<!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>
    <script src="/cdn/expire-1-M/vue/2.6.14/" type="application/javascript"></script>
    <script src="/cdn/expire-1-M/axios/0.26.0/" type="application/javascript"></script>
    <script src="/cdn/expire-1-M/qs/6.10.3/" type="application/javascript"></script>
</head>
<body>
    <div >
        <button @click="onClick1" ref="btn1">ask1</button>
        <button @click="onClick2" ref="btn2">ask2</button>
    </div>
</body>
<script>
// Map object that stores request information and cancel methodconst pendingRequest = new Map();  
//Create the map key based on the requested information (request method, url, request get/post data).function getRequestKey(config){
    const { method, url, params, data } = config;
    return [method, url, (params), (data)].join("&");
}   
//Request an interceptor(
    function (config) {
    //Create the map key based on the requested information (request method, url, request get/post data).    let requestKey = getRequestKey(config)
    //Discern whether the request is repeated    if((requestKey)){
        //Cancel the last request        let cancel = (requestKey)
        cancel()
        //Delete the request information        (requestKey) 
    }
    //Add request information to map    // Generate cancel method     =  || new (cancel => {
        // Add the cancel method to map        if (!(requestKey)) {
            (requestKey, cancel)
        }
    })
    return config;
  },
  (error) => {
     return (error);
  }
);
//Delete the request informationfunction delPendingRequest(config){
    let requestKey = getRequestKey(config)
    if((requestKey)){
        (requestKey)   
    } 
}
//Response Interceptor(
  (response) => {
        //Request succeeds        //Delete the requested information        delPendingRequest()
        return response;
   },
   (error) => {
        //Request failed        //Not a cancel request error        if (!(error)){
            //The server reported an error of 400,500, and deleted the request information            delPendingRequest( || {})
        } 
        return (error);
   }
);
let sendPost = function(data){
    return axios({
        url: '-cloud-studio-demo./test',
        method: 'post',
        data
    })
}
new Vue({
    el: '#app',
    mounted() {
        this.$refs.()
        this.$refs.()
    },
    methods: {
        // Use lodash to prevent the request method        //There is a problem here. It is just that the click event of each button is used separately, but the effect of anti-shake cannot be achieved between the two buttons.        onClick1: async function(){
            let res = await sendPost({username:'zs', age: 20})
            ('Result of request 1', )
        },
        onClick2: async function(){
            let res = await sendPost({username:'zs', age: 20})
            ('Result of Request 2', )
        },
    },
})
</script>
</html>

Preview

The above is the detailed explanation of the implementation example of vue blocking duplicate requests. For more information about vue blocking duplicate requests, please pay attention to my other related articles!