拦截器函数-请求/响应的回调函数调用顺序,代码测试:
// 设置请求拦截器 config 配置对象
axios.interceptors.request.use(function one(config) {
console.log('请求拦截器 成功 - 1号');
return config;
}, function one(error) {
console.log('请求拦截器 失败 - 1号');
return Promise.reject(error);
});
axios.interceptors.request.use(function two(config) {
console.log('请求拦截器 成功 - 2号');
return config;
}, function two(error) {
console.log('请求拦截器 失败 - 2号');
return Promise.reject(error);
});
// 设置响应拦截器
axios.interceptors.response.use(function (response) {
console.log('响应拦截器 成功 1号');
return response;
}, function (error) {
console.log('响应拦截器 失败 1号')
return Promise.reject(error);
});
axios.interceptors.response.use(function (response) {
console.log('响应拦截器 成功 2号')
return response;
}, function (error) {
console.log('响应拦截器 失败 2号')
return Promise.reject(error);
});
//发送请求
axios({
method: 'GET',
url: 'http://localhost:3000/posts'
}).then(response => {
console.log(response);
});
控制台打印的结果:
这里请求拦截器为什么是先2号后1号,而响应拦截器则按顺序输出?(当时我觉得跟堆栈有关),秉着大胆假设,小心求证的探索思想,决定从源码着手分析,究竟藏了啥玄机。
注意: 此流程是通过 promise 串连起来的,请求拦截器传递的是 config, 响应
拦截器传递的是 response。
axios github获取源码 | axios 官方文档 | axios 中文文档
下载后,找到路径下node_modules -》lib -》core -》Axios.js
注意不是dist文件夹下,这个是webpack工具打包输出路径 。
axios.interceptors.request.use说明
首先,搞清楚axios对象里的interceptors属性哪来的?
/**
* Create a new instance of Axios
* 创建 Axios 构造函数
* @param {Object} instanceConfig The default config for the instance
*/
function Axios(instanceConfig) {
//实例对象上的 defaults 属性为配置对象
this.defaults = instanceConfig;
//实例对象上有 interceptors 属性用来设置请求和响应拦截器
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
};
}
Axios 构造函数,通过this关键字指定的属性,另外还有默认配置对象defaults。
第二,request哪来的?
this.interceptors 是个对象,包含两个属性request和response。
第三,use方法哪来的?
request属性对应的值是一个 InterceptorManager实例对象,use是其中的方法。
进入InterceptorManager.js ,调用use方法:
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
this.handlers.push({
fulfilled: fulfilled,
rejected: rejected
});
return this.handlers.length - 1;
};
第四,this.handlers哪来的?
function InterceptorManager() {
//创建一个属性
this.handlers = [];
}
InterceptorManager构造函数,new后添加的属性,是个空数组。
第五,push操作
往this.handles空数组增添一个对象,(两个属性ulfilled和rejected),这就呼应了前边axios调用use方法后所执行的成功和失败回调。
进入Axios.js的request方法中:
Axios.prototype.request = function request(config) {
// ......
// Hook up interceptors middleware
// 创建拦截器中间件 第一个参数用来发送请求, 第二个为 undefined 用来补位
var chain = [dispatchRequest, undefined];
// 创建一个成功的 promise 且成功的值为合并后的请求配置
var promise = Promise.resolve(config);// promise 成功的Promise
// 遍历实例对象的请求拦截器,
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
//将请求拦截器压入数组的最前面
chain.unshift(interceptor.fulfilled, interceptor.rejected);
});
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
//将相应拦截器压入数组的最尾部
chain.push(interceptor.fulfilled, interceptor.rejected);
});
//如果链条长度不为 0
while (chain.length) {
//依次取出 chain 的回调函数, 并执行
promise = promise.then(chain.shift(), chain.shift());
}
return promise;
};
第六,拦截器实例forEach哪来的?
InterceptorManager.prototype.forEach = function forEach(fn) {
utils.forEach(this.handlers, function forEachHandler(h) {
if (h !== null) {
fn(h);
}
});
};
InterceptorManager原型上添加的方法,实际是遍历request对象中handles数组。
第七,chain拦截器中间件分析 。
依自己理解画的示意图(有点丑):
chain初始值有2个元素,分别是[dispatchRequest, undefined]。开始遍历的是request拦截器对象handles数组,追加方式是unshift,将请求拦截器压入数组最前面,而每个对象按照成功回调在前,失败回调在后,此时共有6个元素了;然后遍历response拦截器对象,对象内部放置顺序与上面相同,所以最终一共存了10个元素。
第八,取出chain的回调函数。
依据chain.length判定,成功的回调函数会先执行,所以依次从头往后调用two(config)-》two(error),然后返回一个promise对象;此时数组长度为8,条件为真,接着调用one(config)-》one(error),。。。直至条件不满足。
执行顺序如上,这里指的都是成功的回调,最后客户端拿到promise对象,通过then语法获取请求的数据结果。
在这个过程中,use方法并没有做特别的事情,它只是将两个回调函数保存在request(response)中属性handles这个数组里。
总结一波:在开始调axios(request)时,handles数组把请求拦截器对象往前边放,把响应拦截器往后边放,以跳板的形式(chain初始值)循环遍历取出回调。顺带提一下,如果请求拦截器返回一个失败回调,那么它最终以undefined呈现,发生异常穿透,这是promise的一个特性。