传送门:
- 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源码分析——取消请求