Use Axios to block redundant requests
Stop more for a long time (the company business is busy, 007, really can’t spare time to write blog. Oil maker)
preface
In our day-to-day complex business, we call a lot of back-end apis. To improve server efficiency, we need to reduce the average number of requests per user as much as possible.
The content of this article focuses on the processing of repeated and redundant requests to save network resources. Such as:
- The user frequently clicks a button that sends a request
- The local timed request task sent a new request before the previous request was completed
The second scenario is rare, but I was impressed by a bad test server during a development task that severely affected my development experience.
This problem is usually not exposed when the server resources are abundant, but when the server resources are tight, reducing unnecessary requests can buy more free space for the server.
This article shows the implementation of the “Block unwanted requests” tool, and finally, looks a little bit at what else we can do with this tool.
What do you do
How do you define “duplicate” and “redundant”? When we get data through a request, we don’t want to send a new identical request until the request completes. When we submit data, we want it to be up to date, so we need to cancel the previous request. We expanded the problem and found that the requirements looked like this:
- When the request method is
get
whenx
Prevents a new trigger when the request is not completedx
request - When the request method is not
get
whenx
Request not completed if there are newx
If the request is triggered, the previous one is cancelledx
Request to resend a new onex
Request to ensure that the data sent is up to date - Specific requests can be cleared manually
How to do
The use of tools
We will use the CancelToken API in the AXIos library, which is specifically used to cancel a request that is currently being executed. Document portal
The overall train of thought
Define where the tool should be placed. As a global middleware, it’s perfectly appropriate to put it in axios’ interceptor. We define two methods, cancelReq and resetReq.
CancelReq is responsible for marking ongoing requests and determining whether the same request is being executed. ResetReq is responsible for clearing the flags of completed requests.
import { cancelReq, resetReq } from 'cancel-token.js'
// axios.js
// Request interceptor
_axios.interceptors.request.use(
function(config) {
// * cancelToken prevents duplicate requests
config = cancelReq(config)
return config
},
function(error) {
return Promise.reject(error)
}
)
// Response interceptor
_axios.interceptors.response.use(
function(response) {
CancelToken cancelToken completes the request and clears the token
resetReq(response.config)
},
function(error) {
if (error.config) {
resetReq(error.config)
}
return Promise.reject(error)
}
)
Copy the code
We put this tool in the cancel-token.js file. First we need a queue reqList to hold “requests in progress”. At the same time, we specify a constructor, Req, that holds the key information of a request. Req mainly stores the URL of a request, the method, and the cancel method used to terminate the request.
For some complex business scenarios, if the above parameters are not sufficient to distinguish between each request (for example, if a page needs to send two GET requests for /get/method at the same time, add? Query = a and b? Param =b), we need to add query or data to the Req constructor.
// cancel-token.js
// * Hold the request currently being executed
const reqList = []
/** ** constructor - the key object requested by axios (URL /method/params/data) *@param {*} Config Specifies the current config */ requested by AXIos
function Req(config) {
this.url = config.url
this.method = config.method
this.cancel = config.cancel
// * You may need to add config.query or config.data
}
Copy the code
cancelReq
Method implementation
What cancelReq needs to do is:
- Adds the new request to the executing queue
- duplicative
get
request - If there is a new request to submit data, e.g
post
、put
Cancel the previous request and clear the queue, execute the new request and add it to the queue
CancelReq uses CancelToken. We use the _cancel variable to accept the cancel method and add it to the request config so that we can terminate the request at any time by calling Cancel (‘ This request is over ‘).
When ready, create an instance of Req _req for the request. This _REq will be stored in the reqList execution queue. If the request exists in the queue, follow the logic in the else {} function body below.
// cancel-token.js
import { CancelToken } from 'axios'
// * Prevents requests from being sent again
export function cancelReq(config) {
const _config = config
let _cancel
// * Get the cancel method
_config.cancelToken = new CancelToken(function(cancel) {
/ / assignment
_cancel = cancel
_config.cancel = cancel
})
// Create an instance of Req
const _req = new Req(_config)
// 🎈 Note that the findReq method is used to find if the reQ instance already exists in the execution queue reqList
const _index = findReq(_req)
if (_index === -1) {
// The request is not in the execution state, add it to the execution queue,over
reqList.push(_req)
} else {
// It appears that the request is being executed
if (_req.method.toLowerCase() === 'get') {
// * Block the latter request if method is get
_cancel('Duplicate request cancelled:${_config.url}`)}else {
// * If not get, cancel the previous request and send a new request
reqList[_index].cancel('Duplicate request cancelled:${_config.url}`)
reqList.splice(_index, 1)}}// Finally return the completed config to the AXIos request interceptor
return _config
}
Copy the code
We use a findReq method to determine if there is a reQ in the reqList. Let’s look at the implementation of the findReq method.
/** ** Whether the request to be sent already exists *@param {*} The request that target will make@returns {*} The index of this request, -1, means that there is no */
function findReq(target) {
let _index = -1
for (const i in reqList) {
if (reqList[i].url === target.url &&
reqList[i].method === target.method) {
// * Additional rules can be added
_index = i
break}}return _index
}
Copy the code
resetReq
Method implementation
What resetReq needs to do:
- Clear completed requests
Similar to cancelReq, create an instance of Req, find the request in the reqList and clear it.
// * Clear the request that completed sending
export function resetReq(config) {
const _req = new Req(config)
const _index = findReq(_req)
if(_index ! = = -1) {
reqList.splice(_index, 1)}}Copy the code
A way to manually clear requests
We define a manualCancelReq method when the implementation of some specific business may require us to manually clean up some requirements. This method cleans up requests for specific urls and methods, and can clean up no matching requests (if any). Recursion is used to clean up multiple requests.
import { findIndex as _findIndex } from 'lodash'
/** ** Manually clear one or more possible requests (based on URL matching) *@param {string} Url Indicates the requested URL *@param {string | null} Method Specifies the method of the request, which can be passed null *@param {boolean} Multi Clears multiple requests and passes true */
export function manualCancelReq(url, method, multi) {
const _reqIndex = _findIndex(
reqList,
Object.assign({ url }, method ? { method } : {})
)
if(_reqIndex ! = = -1) {
reqList[_reqIndex].cancel()
reqList.splice(_reqIndex, 1)
// * If multiple requests are cleared
if (multi) {
manualCancelReq(url, method, multi)
}
}
}
Copy the code
Ok, so now your Axios has the ability to block unwanted requests.
extension
So much for the basic functionality. We can use this tool to implement other functions, such as the following “loading animation display when there is an ongoing request”
We need to activate the loading animation by executing the startLoading method in the cancelReq method on each request. When the request completes, the stopLoading method is executed in the resetReq method to turn off the loading animation.
Of course, we need to make some judgments, not to activate the animation repeatedly, and not to turn it off until all requests are complete. At the same time, we can make a little delay for animation activation. For example, animation is not activated in the case of quick request less than 300ms.
Assume that Loading. Start () and Loading. Stop () are activated and disabled, respectively.
// * Whether the page is loading
let loadingStatus = false
// * Timer
let loadingTimeout = null
/** ** Trigger loading-delay ** Conditional: loadingStatus is false *@param {Number} Timeout Specifies the delay ms */ for activating the animation
export function startLoading(timeout) {
if(! loadingStatus) { loadingTimeout =setTimeout(() = > {
Loading.start()
}, timeout || 300)
loadingStatus = true}}/** ** End loading */
export function stopLoading() {
if(loadingStatus && ! reqList.length) {clearTimeout(loadingTimeout)
Loading.stop()
loadingStatus = false}}Copy the code