传送门:
- axios源码简析(一)——axios入口文件
- axios源码简析(二)——Axios类与拦截器
- axios源码简析(三)——请求与取消请求
请求过程
在Axios.prototype.request中我们看到,要先通过请求拦截器,才能进行请求。下面看一下dispatchRequest()是如何实现的
// /lib/core/dispatchRequest.jsmodule.exports = function dispatchRequest(config) {// 判断是否已经取消请求throwIfCancellationRequested(config);/* 对请求的url、headers、data进行处理 */// 发动请求的函数,返回一个promisevar adapter = config.adapter || defaults.adapter;return adapter(config).then(function onAdapterResolution(response) {// 判断是否已经取消请求throwIfCancellationRequested(config);// 处理返回的数据response.data = transformData(response.data,response.headers,config.transformResponse);return response;}, function onAdapterRejection(reason) {if (!isCancel(reason)) {// 判断是否已经取消请求throwIfCancellationRequested(config);// 处理返回的错误信息if (reason && reason.response) {reason.response.data = transformData(reason.response.data,reason.response.headers,config.transformResponse);}}return Promise.reject(reason);}); 如果用户有在配置中传入adapter,将使用defaults.adapter,根据运行环境是浏览器还是nodejs采取不同的请求方式。
// /lib/defaults.js
function getDefaultAdapter() {var adapter;if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {// nodejs环境adapter = require('./adapters/http');} else if (typeof XMLHttpRequest !== 'undefined') {// 浏览器环境adapter = require('./adapters/xhr');}return adapter;
}var defaults = {adapter: getDefaultAdapter(),/* 其他配置 */
};modules.exports = defaults; /lib/adapters/http.js与/lib/adapters/xhr.js两个文件导出的函数都返回一个promise,具体的实现方式就不分析了。里面有很多http请求的细节,可以仔细研究。
取消请求
官方文档中的调用方法
const CancelToken = axios.CancelToken;
const source = CancelToken.source();axios.get('/user/12345', {cancelToken: source.token
}).catch(function(thrown) {if (axios.isCancel(thrown)) {console.log('Request canceled', thrown.message);} else {// handle error}
});axios.post('/user/12345', {name: 'new name'
}, {cancelToken: source.token
})// cancel the request (the message parameter is optional)
source.cancel('Operation canceled by the user.'); 我们进入CancelToken类,找到了CancelToken.source()方法:
// /lib/cancel/CancelTokenCancelToken.source = function source() {var cancel;var token = new CancelToken(function executor(c) {cancel = c;});return {token: token,cancel: cancel};
}; 可以看出,CancelToken.source().token是一个CancelToken类的实例,CancelToken.source().cancel是new CacelToken()时传入参数(一个函数)的参数(也是个函数),通过CancelToken的构造函数可以看出:
// /lib/cancel/CancelTokenfunction 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) {if (token.reason) {// Cancellation has already been requestedreturn;}token.reason = new Cancel(message);resolvePromise(token.reason);});
}
CancelToken.source().cancel就是这个函数:
function cancel(message) {if (token.reason) {// Cancellation has already been requestedreturn;}token.reason = new Cancel(message);resolvePromise(token.reason);
} CancelToken.source().token有promise和reason两个属性,promise 一直处于 pending状态,reason属性是一个Cancel类的实例,Cancel类的构造函数如下:
// /lib/cancel/Cancel.js
function Cancel(message) {this.message = message;
}Cancel.prototype.toString = function toString() {return 'Cancel' + (this.message ? ': ' + this.message : '');
};Cancel.prototype.__CANCEL__ = true; 在源码中,有以下几种方式检测是否执行了取消请求。
1 检测config.cancelToken是否有reason属性,如果有,将reason抛出,axios进入rejected状态。
// /lib/core/dispatchRequest.js
function throwIfCancellationRequested(config) {if (config.cancelToken) {config.cancelToken.throwIfRequested();}
}module.exports = function dispatchRequest(config) {// 判断是否已经取消请求throwIfCancellationRequested(config);/* ... */
};// /lib/cancel/CancelToken
CancelToken.prototype.throwIfRequested = function throwIfRequested() {if (this.reason) {throw this.reason;}
}; 2 在请求过程中,执行CancelToken.source().token的promise属性中的resolve函数,参数是CancelToken.source().token.reason,并将其抛出,promise进入rejected状态
if (config.cancelToken) {// Handle cancellationconfig.cancelToken.promise.then(function onCanceled(cancel) {if (!request) {return;}// 取消请求request.abort();// promise进入rejectedreject(cancel);// Clean up requestrequest = null;});
} 调用方法中catch接到的thrown,就是CancelToken.source().token.reason。
如果在使用axios时候,只在config中添加{cancelToken: source.token},而不调用source.cancel(),则CancelToken.source().token不会有reason属性,CancelToken.source().token.promise也一直是pending状态。请求不会取消。
参考
深入浅出 axios 源码
axios源码分析——取消请求