Scene:
As developers, what we contact most is CRUD, which is the combination of various interfaces. But we seem to pay less attention to the HTTP requests we send. When this request is not fulfilled, and a new request is sent out, how should the current request be handled? We know that in order to prevent repeated actions, we can use things like stabilization and throttling to avoid them, but today we’ll talk about how to avoid repeated requests at the request level, rather than blocking them from the user side. In fact, there are many attempts by developers in this regard on the Internet, but some are not so clear. To this end, after investigating some knowledge of this aspect, combined with their own peacetime encounter in the development of the scene. Summarize the two most common scenarios where HTTP requests need to be canceled
Scene 1:
The same request needs to be cancelled, so the same request means method, params, URL for get requests, method, body, and URL for Post requests
Scene 2:
Routing address changed (previous page request is invalid)
Implementation method:
First of all, in order to achieve repeated cancellation, we need to do two steps. The first step is to know how to cancel, and the second step is to determine the current request is repeated
Cancel duplicate request implementation method:
As for how to cancel, I will explain this part from axios and FETCH, because the cancellation methods of these two request methods are different
React is more straightforward because we know the hook function useEffect. UseEffect means that the return function will clean up every time useEffect is used, so in this case, we put the cancel operation here. You can simulate canceling the previous operation each time
axios
In a word, the essence of axios cancelToken is to use its internal encapsulation cancelToken.
The first thing to know is that the token establishes uniqueness, which is also the token that determines which request needs to be cancelled. This can be generated by canceltoken.source ()
The source contains the cancel method, which we call to cancel
useEffect(() => {
const cancelToken = axios.CancelToken;
const source = cancelToken.source();
setAxiosRes("axios request created");
getReq(source).then((res) => {
setAxiosRes(res);
});
return () => {
source.cancel("axios request cancelled");
};
}, [axiosClick]);
Copy the code
export const instance = axios.create({ baseURL: "http://localhost:4001", }); export const getReq = async (source) => { try { const { data } = await instance.get("/", { cancelToken: source.token, }); return data; } catch (err) { if (axios.isCancel(err)) { return "axios request cancelled"; } return err; }};Copy the code
What needs to be noted here is that the action of cancel itself can be caught by the catch part, and it is also an Err. We use the isCancel method provided by it to determine whether it is a cancellation operation, which can be used to verify whether our cancellation is successful
fetch:
AbortController() can cancel all requests that respond to signal. React is also used to simulate the fetch. In essence, the fetch is still the same and can also be caught
export const instance = axios.create({
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
setFetchRes("fetch request created");
hitApi(signal).then((res) => {
setFetchRes(res);
});
//cleanup function
return () => {
controller.abort();
};
}, [fetchClick]);
Copy the code
The hitApi function puts signal in our fetch so that we abort.
export const hitApi = async (signal) => { try { const response = await fetch("http://localhost:4001/", { signal }); const data = await response.json(); return data; } catch (err) { if (err.name === "AbortError") { return "Request Aborted "; } return err; }}Copy the code
Here again, ‘AbortError’ can be caught in catch
Check for duplication
Now that the cancel function is complete, it’s time to consider how to determine if the request is repeated. Of course, to determine whether it is a duplicate request, you want constant time complexity to find whether there is a duplicate request, of course, using Map, so that you can find the duplicate request at order (1) speed, and then you can decide to cancel it. And, as you can imagine, the whole process is adding things to the array, and the things that have been canceled of course need to be taken out, so we need a function to add, and a function to remove
const addPending = (config) => { const url = [ config.method, config.url, qs.stringify(config.params), qs.stringify(config.data) ].join('&') config.cancelToken = config.cancelToken || new axios.CancelToken(cancel => { if (! pending.has(url)) { // If the current request does not exist in pending, add it pending.set(url, cancel) } }) }Copy the code
CancelToken when assigning config.cancelToken, note whether the current config.cancelToken already has a value
To facilitate our tiling of parameters, we can use QS to convert Object to string
const removePending = (config) => {
const url = [
config.method,
config.url,
qs.stringify(config.params),
qs.stringify(config.data)
].join('&')
if (pending.has(url)) { // If the current request identity exists in pending, you need to cancel the current request and remove it
const cancel = pending.get(url)
cancel(url)
pending.delete(url)
}
}
Copy the code
Axios interceptor
In real projects, however, we usually have axios interceptors to unify our requests, so many people here like to add these two methods directly to the Axios interceptor, once and for all.
axios.interceptors.request.use(config => {
removePending(options) // Check previous requests to cancel before the request starts
addPending(options) // Add current request to pending
// other code before request
return config
}, error => {
return Promise.reject(error)
})
Copy the code
axios.interceptors.response.use(response => {
removePending(response) // Remove this request at the end of the request
return response
}, error => {
if (axios.isCancel(error)) {
console.log('repeated request: ' + error.message)
} else {
// handle error code
}
return Promise.reject(error)
})
Copy the code
Route switchover mode
If the pending queue is blocked, the pending queue will be empty. If the pending queue is blocked, the pending queue will be empty.
export const clearPending = () => {
for (const [url, cancel] of pending) {
cancel(url)
}
pending.clear()
}
Copy the code
router.beforeEach((to, from, next) => {
clearPending()
// ...
next()
})
Copy the code
Effect demonstration:
As you can see, the request is cancelled for repeated clicks, whereas 200 is returned on success.
Principle analysis:
function CancelToken(executor) { if (typeof executor ! == 'function') { throw new TypeError('executor must be a function.'); } var resolvePromise; this.promise = new Promise(function promiseExecutor(resolve) { resolvePromise = resolve; // Expose the interior}); var token = this; / / executor (cancel method); executor(function cancel(message) { if (token.reason) { // Cancellation has already been requested return; } //token.reason is an instance of Cancel token.reason = new Cancel(message); resolvePromise(token.reason); // Change the state}); }Copy the code
The core of CancelToken is essentially to mount a promise, and instead of actively resolve or reject, expose the initiative first, namely a resolvePromise in the code, and then change the state of the promise in the CancelToken function.
The xhrAdapter source code is used to see where abort is made. The red part is the process that is executed by changing the promise state,.then.
function xhrAdapter(config) { return new Promise(function dispatchXhrRequest(resolve, Reject) {if (config.cancelToken) { Listening cancelToken promise in the state change the config. CancelToken. Promise. Then, the function onCanceled (cancel) {if (! request) { return; } request.abort(); reject(cancel); request = null; }); }})}Copy the code
Conclusion:
HTTP request to cancel is not actually very strange thing, to achieve similar requests to cancel, there are many other ways, and even a lot of method is better than that of a kind of implementation method, such as cancel the current request instead of cancel the previous one, seems to be more logical, but this article focuses on the thought of this kind of exposure to resolve, very worth learning.
The text/Lily
Pay attention to the technology, do the most fashionable technology!