axios拦截器工作方式及原理源码解析
锋利的绵羊 人气:0axios 拦截器的配置方式
本文所用 axios 版本号为:1.3.2
。
axios 中有两种拦截器:
axios.interceptors.request.use(onFulfilled, onRejected, options)
:配置请求拦截器。- onFulfilled 方法在发送请求前执行,接收 config 对象,返回一个新的 config 对象,可在此方法内修改 config 对象。
- onRejected 方法在 onFulfilled 执行错误后执行,接收 onFulfilled 执行后的错误对象。
- options 配置参数
- synchronous:控制请求拦截器是否为异步执行,默认为 true,每个拦截器都可以单独设置,只要有一个设置为 false 则为同步执行,否则为异步执行。
- runWhen:一个方法,接收 config 对象作为参数,在每次调用设定的拦截器方法前调用,返回结果为 true 时执行拦截器方法。
axios.interceptors.response.use(onFulfilled, onRejected, options)
:配置响应拦截器。- onFulfilled 方法在返回响应结果前调用,接收响应对象,返回一个新的响应对象,可在此方法内修改响应对象。
- onRejected 与 options 同
axios.interceptors.request.use
。
axios.interceptors.request.use( function (config) { return config; }, function (error) { return Promise.reject(error); } ); axios.interceptors.response.use( function (response) { return response; }, function (error) { return Promise.reject(error); } );
可以添加多个拦截器:
axios.interceptors.request.use( function (config) { throw new Error("999"); return config; }, function (error) { console.log(1, error); return Promise.reject(error); } ); axios.interceptors.request.use( function (config) { throw new Error("888"); return config; }, function (error) { console.log(2, error); return Promise.reject(error); } ); axios.interceptors.response.use( function (response) { return response; }, function (error) { console.log(3, error); return Promise.reject(error); } ); axios.interceptors.response.use( function (response) { return response; }, function (error) { console.log(4, error); return Promise.reject(error); } ); axios.get("https://www.baidwwu.com").catch((error) => { console.log(4, error); }); // 2 Error: 888 // 1 Error: 888 // 3 Error: 888 // 4 Error: 888 // 5 Error: 888
先执行请求拦截器,后执行响应拦截器。
- 设置多个请求拦截器的情况:后添加的拦截器先执行。
- 设置多个响应拦截器的情况:按照设置顺序执行。
use() 方法的定义
先来看 use() 方法相关代码:
this.interceptors = { request: new InterceptorManager$1(), response: new InterceptorManager$1(), }; var InterceptorManager = (function () { function InterceptorManager() { this.handlers = []; } // ... // _createClass 方法可以给对象的原型上添加属性 _createClass(InterceptorManager, [ { key: "use", value: function use(fulfilled, rejected, options) { this.handlers.push({ fulfilled: fulfilled, rejected: rejected, synchronous: options ? options.synchronous : false, runWhen: options ? options.runWhen : null, }); return this.handlers.length - 1; }, }, { key: "forEach", value: function forEach(fn) { utils.forEach(this.handlers, function forEachHandler(h) { if (h !== null) { fn(h); } }); }, }, // ... ]); return InterceptorManager; })(); var InterceptorManager$1 = InterceptorManager;
可以看到 interceptors.request
和 interceptors.response
各自指向 InterceptorManager
的实例。
InterceptorManager
原型上设置了 use()
方法,执行后给待执行队列 this.handlers
中添加一条数据。
这里利用了 this
的特性来区分作用域:谁调用 use()
方法就给谁的 handlers
中添加数据。在调用 use()
方法之后,已经将回调函数按顺序添加到了 handlers
数组中。
forEach()
方法用来遍历当前作用域 handlers
中不为 null
的元素,在执行拦截器时有用到,详情见下文。
拦截器如何执行
拦截器是在调用了 request()
方法前后执行的,先看相关源码:
var Axios = (function () { _createClass(Axios, [ { key: "request", value: function request(configOrUrl, config) { // ... // 初始化请求拦截器 var requestInterceptorChain = []; var synchronousRequestInterceptors = true; this.interceptors.request.forEach(function unshiftRequestInterceptors( interceptor ) { if ( typeof interceptor.runWhen === "function" && interceptor.runWhen(config) === false ) { return; } synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous; requestInterceptorChain.unshift( interceptor.fulfilled, interceptor.rejected ); }); // 初始化响应拦截器 var responseInterceptorChain = []; this.interceptors.response.forEach(function pushResponseInterceptors( interceptor ) { responseInterceptorChain.push( interceptor.fulfilled, interceptor.rejected ); }); var promise; var i = 0; var len; // 请求拦截器同步执行模式 if (!synchronousRequestInterceptors) { var chain = [dispatchRequest.bind(this), undefined]; chain.unshift.apply(chain, requestInterceptorChain); chain.push.apply(chain, responseInterceptorChain); len = chain.length; promise = Promise.resolve(config); console.log(11, chain); while (i < len) { promise = promise.then(chain[i++]).catch(chain[i++]); } return promise; } // 请求拦截器异步执行模式 len = requestInterceptorChain.length; var newConfig = config; i = 0; while (i < len) { var onFulfilled = requestInterceptorChain[i++]; var onRejected = requestInterceptorChain[i++]; try { newConfig = onFulfilled(newConfig); } catch (error) { onRejected.call(this, error); break; } } try { promise = dispatchRequest.call(this, newConfig); } catch (error) { return Promise.reject(error); } i = 0; len = responseInterceptorChain.length; while (i < len) { promise = promise.then( responseInterceptorChain[i++], responseInterceptorChain[i++] ); } return promise; }, }, ]); })();
上面是相关的全部代码,下面进行分解。
拦截器回调方法的添加顺序
var requestInterceptorChain = []; // 请求拦截器执行链 // 是否同步执行请求拦截器,每个拦截器都可以单独设置,但是只有所有拦截器都设置为true才为true var synchronousRequestInterceptors = true; this.interceptors.request.forEach(function unshiftRequestInterceptors( interceptor ) { // 传入 runWhen() 方法时,如果 runWhen() 方法返回值为 false 则忽略这个请求拦截器 if ( typeof interceptor.runWhen === "function" && interceptor.runWhen(config) === false ) { return; } // 是否同步执行 synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous; // 用 unshift() 添加数据,后设置的拦截器在前 requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected); }); var responseInterceptorChain = []; // 响应拦截器执行链 this.interceptors.response.forEach(function pushResponseInterceptors( interceptor ) { // 响应拦截器按顺序加在后面 responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected); });
interceptors.request.forEach()
的定义在上文提到过,封装为遍历自身的 handlers
数组,与数组的 forEach
类似,只是过滤了值为 null
的元素。
runWhen()
方法接收 config,返回 boolean,控制是否将当前拦截器的回调函数添加到执行链中。
请求拦截器用 unshift()
方法添加,所以后设置的先执行,响应拦截器用 push()
方法添加,所以按照设置顺序执行。
只要有一个拦截器的 synchronous
设置为 false
,则 synchronousRequestInterceptors
的值为 false
。
同步执行请求拦截器(顺序执行)
synchronousRequestInterceptors
为 false
时为同步执行,相关逻辑如下:
var promise; var i = 0; var len; if (!synchronousRequestInterceptors) { var chain = [dispatchRequest.bind(this), undefined]; // 执行链 chain.unshift.apply(chain, requestInterceptorChain); // 将请求拦截器添加到请求之前 chain.push.apply(chain, responseInterceptorChain); // 将响应拦截器添加到响应之后 len = chain.length; promise = Promise.resolve(config); while (i < len) { promise = promise.then(chain[i++], chain[i++]); // 用 Promise 包装执行链 } return promise; }
dispatchRequest()
是发送请求的方法。
同步执行模式下,会将执行链中的所有方法用 Promise 进行封装,前一个方法执行完毕后将其返回值作为参数传递给下一个方法。
这里的 chain
其实就是所有拦截器方法与请求方法合并而成的执行链,等价于: [...requestInterceptorChain, dispatchRequest.bind(this), ...responseInterceptorChain]
。
从一个例子来看 chain
的成员:
axios.interceptors.request.use(onFulfilled1, onRejected1); axios.interceptors.request.use(onFulfilled2, onRejected2); axios.interceptors.response.use(onFulfilled3, onRejected3); axios.interceptors.response.use(onFulfilled4, onRejected4); axios.get(); // chain: [onFulfilled2, onRejected2, onFulfilled1, onRejected1, dispatchRequest, onFulfilled3, onRejected3, onFulfilled4, onRejected4]
在构建 Promise 链的时候,一次遍历中取了两个方法传递给 then():
promise = Promise.resolve(config); while (i < len) { promise = promise.then(chain[i++], chain[i++]); // 用 Promise 包装执行链 } return promise;
异步执行请求拦截器(同时执行)
var promise; var i = 0; var len = requestInterceptorChain.length; // 请求执行链的长度 var newConfig = config; i = 0; // 执行请求拦截器回调 while (i < len) { var onFulfilled = requestInterceptorChain[i++]; // use() 的第一个参数 var onRejected = requestInterceptorChain[i++]; // use() 的第二个参数 // 执行成功后继续执行,执行失败后停止执行。 try { newConfig = onFulfilled(newConfig); } catch (error) { onRejected.call(this, error); break; } } // 执行发送请求方法 try { promise = dispatchRequest.call(this, newConfig); } catch (error) { return Promise.reject(error); // 执行失败后退出 } // 执行响应拦截器回调 i = 0; len = responseInterceptorChain.length; while (i < len) { promise = promise.then( responseInterceptorChain[i++], // use() 的第一个参数 responseInterceptorChain[i++] // use() 的第二个参数 ); } return promise;
dispatchRequest()
是发送请求的方法。
用 while
循环遍历所有的请求拦截器并调用,将执行语句包裹在 try-catch
语句中,只要有一个请求拦截器异常就停止循环。
可以看到在异步模式下,请求拦截器为异步执行,但是不影响发送请求,而响应拦截器还是在请求响应后同步执行。
Q&A
拦截器是如何工作的
调用 .request.use()
和 .response.use()
方法时,将传入的拦截器回调方法分别存入 请求拦截器回调数组
和 响应拦截器回调数组
。
在调用 .get()
等方法时,将 请求拦截器回调数组
和 响应拦截器回调数组
与发送请求的方法拼接成一个完整的执行链,按照同步或异步的模式调用执行链中的方法。
响应拦截器总是在返回响应结果后按顺序执行。
请求拦截器根据 synchronous
配置不同,行为有所不同:
- 异步执行请求拦截器模式。
- 没有设置
synchronous
时默认为异步执行请求拦截器模式,即遍历执行所有的请求拦截器一参回调,执行报错后停止遍历,并执行该拦截器的二参回调。 - 同步执行请求拦截器模式。
- 设置
synchronous
为false
时为同步执行请求拦截器模式,将执行链包装成一个 Promise 链顺序执行。
拦截器的执行顺序
先执行请求拦截器,后执行响应拦截器。
- 设置多个请求拦截器的情况:后添加的拦截器先执行。
- 设置多个响应拦截器的情况:按照设置顺序执行。
同步&异步
计算机中的同步,指的是现实中的一步一步(同一时间只能干一件事),异步指的是同时进行(同一时间能干多件事)。
参考 npm axios
加载全部内容