以比较常用的前端 request 库:axios 为例:
场景:
页面中有四个 Tab,当用户进入页面的时候,会加载默认tab 的数据。
当用户并不想看这个 tab 的数据,而是想切换到其它 tab的 时候,这时候就需要将前一个tab 的请求取消掉,否则可能会出现:
因为四个 Tab 的数据结构都相同,第二个 Tab 的请求数据量比较小,而第一个 Tab 的请求的数据量比较大,导致第一个 Tab的请求的 response 比 第二个请求的response 晚到。
页面中已经渲染好了 第二个 Tab的数据,结果用户在浏览第二个 Tab的数据的时候,第一个 Tab的数据这时候来了,把第二个 Tab的数据给覆盖了,这时候,用户就完全懵圈了,数据就完全错乱了
解决方案
当用户切换 Tab 的时候,需要将前一个 Tab 取消
思路
- 建立队列
- 为每一个请求生唯一ID(或者存储 请求的URL、请求参数等信息),将该请求 push 到这个 队列中
上面ID 的作用其实是,有时候页面可能会同时有一些和 Tab1、Tab2 无关的请求,我们并希望在取消 Tab1相关请求的时候,将无关请求也取消了。因此我们可以传入 ID,这样就能指定取消某个具体的请求了。当然根据 URL和参数来取消特定请求也是 OK 的
- 点击 Tab1, 将 Tab1 相关的请求push 到队列中;如果在请求过程中,用户点击了 Tab2,则遍历队列,调用请求的 cancel 方法,取消该请求
代理演示
// 取消重复请求
// pending: Array<{ url: string; cancel: Function; }>
const pending = [];
window.pendingRequests = pending;
const CancelToken = axios.CancelToken;
const removePending = config => {
let pendingLen = pending.length;
while (pendingLen--) {
// for (const index in pending) {
const item = pending[pendingLen];
// 当前请求在数组中存在时执行函数体
if (item.url === fmtUrlByConfig(config)) {
// 执行取消操作
item.cancel();
// 从数组中移除记录
pending.splice(pendingLen, 1);
}
}
};
// http request 拦截器
axios.interceptors.request.use(
config => {
removePending(config);
config.cancelToken = new CancelToken(c => {
pending.push({
url: getUrlByPathAndParams(config),
cancel: c,
});
});
return config;
},
error => {
// Do something with request error
return Promise.reject(error);
}
);