2021-09-06 22:44:42
在 Web 项目开发过程中,我们经常会遇到重复请求的场景,如果系统不对重复的请求进行处理,则可能会导致系统出现各种问题。比如重复的 post 请求可能会导致服务端产生两笔记录。那么重复请求是如何产生的呢?这里我们举 2 个常见的场景:
· 假设页面中有一个按钮,用户点击按钮后会发起一个 AJAX 请求。如果未对该按钮进行控制,当用户快速点击按钮时,则会发出重复请求。
· 假设在考试结果查询页面中,用户可以根据 “已通过”、“未通过” 和 “全部” 3 种查询条件来查询考试结果。如果请求的响应比较慢,当用户在不同的查询条件之前快速切换时,就会产生重复请求。
既然已经知道重复请求是如何产生的,也知道了它会带来一些问题。接下来,小编将以 Axios 为例,带大家来一起解决重复请求的问题。
由于完整的示例代码内容比较多,阿宝哥就不放具体的代码了。感兴趣的小伙伴,可以访问以下地址浏览示例代码。
完整的示例代码:https://gist.github.com/semlinker/e426780664f0186db434882f1e27ac3a
这里我们来看一下 Axios 取消重复请求示例的运行结果:
从上图可知,当出现重复请求时,之前已发送且未完成的请求会被取消掉。下面我们用一张流程图来总结一下取消重复请求的处理流程:
最后,我们来回答前面留下的问题,即 CancelToken 内部是如何工作的?
在前面的示例中,我们是通过调用 CancelToken 构造函数来创建 CancelToken 对象:
new axios.CancelToken((cancel) => {
if (!pendingRequest.has(requestKey)) {
pendingRequest.set(requestKey, cancel);
}
})
所以接下来,我们来分析 CancelToken 构造函数,该函数被定义在 lib/cancel/CancelToken.js 文件中:
// lib/cancel/CancelToken.js
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;
});
var token = this;
executor(function cancel(message) { // 设置cancel对象
if (token.reason) {
return; // Cancellation has already been requested
}
token.reason = new Cancel(message);
resolvePromise(token.reason);
});
}
由以上代码可知,cancel 对象是一个函数,当我们调用该函数后,会创建 Cancel 对象并调用 resolvePromise 方法。该方法执行后,CancelToken 对象上 promise 属性所指向的 promise 对象的状态将变为 resolved。那么这样做的目的是什么呢?这里我们从 lib/adapters/xhr.js 文件中找到了答案:
// lib/adapters/xhr.js
if (config.cancelToken) {
config.cancelToken.promise.then(function onCanceled(cancel) {
if (!request) { return; }
request.abort(); // 取消请求
reject(cancel);
request = null;
});
}
看完上述的内容,可能有的小伙伴还不是很能理解 CancelToken 的工作原理,所以阿宝哥又画了一张图来帮助大家理解 CancelToken 的工作原理:
更多精彩内容关注公众号《前端学习栈》,干货内容与你分享!