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 aslodash
ofdebounce
Methods 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 callingXMLHttpRequest
Object instanceabort
Method 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
passaxios
ofCancelToken
Object instancecancel
Method 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
passaxios
The 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
passaxios
The 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
passaxios
The 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!