前言
以前刚入行时,个人单打独斗为这个问题探索了好久,还写了一篇文章记载过程,想想仿佛还在昨天。没办法现在我们要卷,不能只知其然,不知其所以然。在验证的过程中也加深了自己背的八股文的理解。验证地址
复习下axios 取消请求两种方式
- 一定要看文档、看文档、看文档。
- 我开始在 serve 里面用 koa 使接口返回 sleep(2000);然后想着 2s 够我点取消按钮了吧。实验才发现请求是瞬发的,除非我自己网络有问题发不出去。所以就换条路子,浏览器肯定有并发限制的。直接冲个100条看看。我就不信你不进队列等待。果然成功了。可以想象以前理解的是多么肤浅。
utils/axios.js
import axios from 'axios';
const CancelToken = axios.CancelToken;
export const cancelTokenMap = new Map();
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
// 取消方法一
config.cancelToken = new CancelToken(function executor(c) {
cancelTokenMap.set(config.url, c.bind(null, config.url));
})
// 取消方法二
// const source = CancelToken.source();
// config.cancelToken = source.token;
// cancelTokenMap.set(config.url, source.cancel);
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
App.vue
const cancelAll = () => {
// 请欣赏封面图那令人沉醉的中国红
Array(100).fill(1).forEach((el,i) => {
const firstCancel = cancelTokenMap.get(`/sendFirst?queryKey=${i}`);
firstCancel && firstCancel();
});
}
const sendAjax = () => {
Array(100).fill(1).forEach((el,i) => {
sendFirst(i).then(() => {
// console.log('sendFirst--', res)
});
})
}
知其然知其所以然 cancel 流程
- 用户设置 config.cancelToken = new CancelToken(function executor(cancel){});
- xhr.js xhrAdapter() 里 config.cancelToken.subscribe(onCanceled) 订阅取消事件
- cancelToken.js CancelToken.prototype.subscribe 使用 this._listeners。存储订阅的事件;
- new CancelToken(functionon executor(cancel){}) 用户执行 cancel 函数;
- resolvePromise(token.reason)就会执行,触发 this.promise.then
- this.promise.then 里面 this._listeners 执行,那么 onCanceled就会执行 -> request.abort(); 自此整个串起来了,到这里就会把请求取消,不会执行到下面 request.send(requestData);
- 至此完成了 request 的取消。
入口文件 axios.js
axios.js
// Expose Cancel & CancelToken
axios.Cancel = require('./cancel/Cancel');
axios.CancelToken = require('./cancel/CancelToken');
axios.isCancel = require('./cancel/isCancel');
axios.VERSION = require('./env/data').version;
cancel/CancelToken.js
// 不长 50行代码
function CancelToken(executor) {
var resolvePromise;
var token = this;
this.promise = new Promise(function promiseExecutor(resolve) {
resolvePromise = resolve;
});
this.promise.then(function(cancel) {
var i;
var l = token._listeners.length;
for (i = 0; i < l; i++) {
token._listeners[i](cancel);
}
token._listeners = null;
});
executor(function cancel(message) {
if (token.reason) {
// Cancellation has already been requested
return;
}
token.reason = new Cancel(message);
resolvePromise(token.reason);
});
}
adapters/xhr.js
// 先只看客户端部分,node 是放在 http.js 一样。axios 使用适配器来统一两端的实现.
function xhrAdapter(config) {
new Promise(function dispatchXhrRequest(resolve, reject) {
if (config.cancelToken || config.signal) {
onCanceled = function(cancel) {
if (!request) {
return;
}
reject(!cancel || (cancel && cancel.type) ? new Cancel('canceled') : cancel);
request.abort();
request = null;
};
config.cancelToken && config.cancelToken.subscribe(onCanceled);
if (config.signal) {
config.signal.aborted ? onCanceled() : config.signal.addEventListener('abort', onCanceled);
}
// Send the request
request.send(requestData);
}
})
}
我们学到什么
- import axios from 'axios' 项目全局都是同一个对象,
axios.js: axios = new Axios(config); export default axios;
- 适配器方法 抹平客户端 xhr.js 与 node 端 http.js 的差异 实现方法统一;
- 发布订阅模式 cancelToken.js _listeners/subscribe
- cancelToken.js this.promise 的巧妙利用,把控制权交给用户。
- cancelToken.js source是构造函数方法也是基于 cancelToken 实例来实现提供更多选择。
- 存下封面图片链接