This article is the fourth in a series of in-depth ahoos source code articles, which have been organized into document- address . I think it's not bad, give me a follow to support it, thanks.
This article explores the useLockFn of ahooks. And thus discuss a very common scenario, canceling repeated requests.
Scenes
Imagine that there is such a scenario, there is a form, you may submit multiple times, it is likely to lead to incorrect results.
There are many ways to solve this kind of problem, such as adding loading, you can't click again after the first click. Another method is to add a static lock to the request asynchronous function to prevent concurrency. This is what ahooks' useLockFn does.
useLockFn
useLockFn
is used to add a race lock to an asynchronous function to prevent concurrent execution.
Its source code is relatively simple, as follows:
import { useRef, useCallback } from 'react';
// 用于给一个异步函数增加竞态锁,防止并发执行。
function useLockFn<P extends any[] = any[], V extends any = any>(fn: (...args: P) => Promise<V>) {
// 是否现在处于一个锁中
const lockRef = useRef(false);
// 返回的是增加了竞态锁的函数
return useCallback(
async (...args: P) => {
// 判断请求是否正在进行
if (lockRef.current) return;
// 请求中
lockRef.current = true;
try {
// 执行原有请求
const ret = await fn(...args);
// 请求完成,状态锁设置为 false
lockRef.current = false;
return ret;
} catch (e) {
// 请求失败,状态锁设置为 false
lockRef.current = false;
throw e;
}
},
[fn],
);
}
export default useLockFn;
As you can see, its input parameter is an asynchronous function, and the returned function is a function that adds a race lock. Make a flag through lockRef, and its value is false when initialized. When the request is being made, it is set to true, so that when the function is called next time, it will return directly without executing the original function, so as to achieve the purpose of locking.
shortcoming
Although it is practical, the disadvantage is obvious. I need to manually add it to each request asynchronous function that needs to add a race lock . Is there a more general and convenient method?
The answer is that repeated requests can be automatically canceled through axios.
axios automatically cancels duplicate requests
axios cancel request
For the HTTP request initiated by the native XMLHttpRequest object, the abort method of the XMLHttpRequest object can be called.
So what about axios commonly used in our projects? In fact, it also uses the XMLHttpRequest object at the bottom, and it exposes the CancelToken API to cancel the request. Can be used as follows:
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.post('/user/12345', {
name: 'gopal'
}, {
cancelToken: source.token
})
source.cancel('Operation canceled by the user.'); // 取消请求,参数是可选的
Another way to use it is to call the constructor of CancelToken to create CancelToken, which is used as follows:
const CancelToken = axios.CancelToken;
let cancel;
axios.get('/user/12345', {
cancelToken: new CancelToken(function executor(c) {
cancel = c;
})
});
cancel(); // 取消请求
How to automatically cancel duplicate requests
Knowing how to cancel the request, how to do it automatically? The answer is through the interceptor of axios.
- Request Interceptor: The role of this type of interceptor is to perform certain operations uniformly before the request is sent, such as adding token-related fields to the request header.
- Response interceptor: The function of this type of interceptor is to perform certain operations uniformly after receiving the server response, such as automatically jumping to the login page when the response status code is found to be 401.
The specific method is as follows:
The first step is to define several important auxiliary functions.
-
generateReqKey
: used to generate the request key according to the current request information. Only the same key will be judged as a duplicate request . This is very important and may be related to specific business scenarios. For example, there is a request, the input box is fuzzy searched, the user frequently enters keywords, and multiple requests are sent at one time. Search results are not as expected. In fact, you only need to judge it as a repeated request according to the URL and request method, and then cancel the previous request.
Here I think, if necessary, an API can be exposed for developers to customize repeated rules. Here we first generate a unique key based on the request method, url, and parameters to do it.
function generateReqKey(config) {
const { method, url, params, data } = config;
return [method, url, Qs.stringify(params), Qs.stringify(data)].join("&");
}
addPendingRequest. Used to add the current request information to the pendingRequest object.
const pendingRequest = new Map(); function addPendingRequest(config) { const requestKey = generateReqKey(config); config.cancelToken = config.cancelToken || new axios.CancelToken((cancel) => { if (!pendingRequest.has(requestKey)) { pendingRequest.set(requestKey, cancel); } }); }
removePendingRequest. Check if there are duplicate requests, and if so, cancel the sent requests .
function removePendingRequest(config) { const requestKey = generateReqKey(config); if (pendingRequest.has(requestKey)) { const cancelToken = pendingRequest.get(requestKey); cancelToken(requestKey); pendingRequest.delete(requestKey); } }
The second step is to add a request interceptor.
axios.interceptors.request.use(
function (config) {
removePendingRequest(config); // 检查是否存在重复请求,若存在则取消已发的请求
addPendingRequest(config); // 把当前请求信息添加到pendingRequest对象中
return config;
},
(error) => {
return Promise.reject(error);
}
);
The second step is to add a response interceptor.
axios.interceptors.response.use(
(response) => {
removePendingRequest(response.config); // 从pendingRequest对象中移除请求
return response;
},
(error) => {
removePendingRequest(error.config || {}); // 从pendingRequest对象中移除请求
if (axios.isCancel(error)) {
console.log("已取消的重复请求:" + error.message);
} else {
// 添加异常处理
}
return Promise.reject(error);
}
);
At this point, we have completed the function of automatically canceling repeated requests through axios.
Thinking and Summarizing
Although the problem of repeated requests can be solved by adding a race lock to the request function through a hook or method like useLockFn. But this still depends on the developer's habits. If there are no rules, it is difficult to avoid problems.
Through the axios interceptor and its CancelToken function, we can automatically cancel the sent request in the interceptor. Of course, if there are some interfaces that need to send requests repeatedly, you can consider adding a whitelist function to prevent the request from being canceled.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。