接口分析
https://github.com/axios/axio...
一般用法
// Add a request interceptoraxios.interceptors.request.use(function (config) { // Do something before request is sent return config; }, function (error) { // Do something with request error return Promise.reject(error); });// Add a response interceptoraxios.interceptors.response.use(function (response) { // Any status code that lie within the range of 2xx cause this function to trigger // Do something with response data return response; }, function (error) { // Any status codes that falls outside the range of 2xx cause this function to trigger // Do something with response error return Promise.reject(error); });
删除拦截器
const myInterceptor = axios.interceptors.request.use(function () {/*...*/});axios.interceptors.request.eject(myInterceptor);
同步解决
axios.interceptors.request.use(function (config) { config.headers.test = 'I am only a header!'; return config;}, null, { synchronous: true });
条件执行
function onGetCall(config) { return config.method === 'get';}axios.interceptors.request.use(function (config) { config.headers.test = 'special get headers'; return config;}, null, { runWhen: onGetCall });
提出问题:
a. interceptor是如何实现申请的前置解决和后置解决?
b. 为什么request interceptor要思考同步执行?
https://github.com/axios/axio...里提到,默认synchronous为false,是异步执行的。当js主线程梗塞的时候,就会导致提早执行request interceptor。这里波及到了event loop模型的常识,js引擎会先执行同步代码,执行完了,会去查看工作队列,看看有没有异步工作的后果,有的话推送到执行栈执行。那咱们上面看看它是如何实现同步执行的。
源码剖析
- 提取关键字
interceptors
,定位到Axios.js
、InterceptorManager.js
查看InterceptorManager.js
var utils = require("./../utils");function InterceptorManager() { this.handlers = [];}InterceptorManager.prototype.use = 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;};// 重置interceptor为null,留神不是删除InterceptorManager.prototype.eject = function eject(id) { if (this.handlers[id]) { this.handlers[id] = null; }};// 专属的递归器,次要逻辑就是如果不是null就执行执行器InterceptorManager.prototype.forEach = function forEach(fn) { utils.forEach(this.handlers, function forEachHandler(h) { if (h !== null) { fn(h); } });};module.exports = InterceptorManager;
顺便贴一下util.js的forEach办法,实质上就是对obj进行for循环,对每个元素执行回调
function forEach(obj, fn) { // Don't bother if no value provided if (obj === null || typeof obj === 'undefined') { return; } // Force an array if not already something iterable if (typeof obj !== 'object') { /*eslint no-param-reassign:0*/ obj = [obj]; } if (isArray(obj)) { // Iterate over array values for (var i = 0, l = obj.length; i < l; i++) { fn.call(null, obj[i], i, obj); } } else { // Iterate over object keys for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { fn.call(null, obj[key], key, obj); } } }}
小结一下,InterceptorManager类,设计了收集、移除、递归interceptor的性能
再看Axios.js
line18~21:初始化interceporthis.interceptors = { request: new InterceptorManager(), response: new InterceptorManager()};
line60~118: 过滤interceptors,并合并解决
var requestInterceptorChain = [];var synchronousRequestInterceptors = true;// 用InterceptorManager的forEach办法,曾经过滤为null的interceptorthis.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) { if (typeof interceptor.runWhen === 'function' && interceptor.runWhen(config) === false) { return; } // 对于同步执行的request相干的interceptor逻辑,前面咱们再回头来看。这里的逻辑就是要么全副是同步执行,要么全副是异步执行。只有有一个是异步,则全副用异步的办法执行。 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;// request的interceptor以异步模式执行if (!synchronousRequestInterceptors) { // 这里要结构[onFullfilled, onRejected]格局,以便下边应用promise.then办法来执行。 var chain = [dispatchRequest, undefined]; // 合并操作:先执行request的interceptor,再发动申请,最初再执行response的interceptor Array.prototype.unshift.apply(chain, requestInterceptorChain); chain.concat(responseInterceptorChain); // 新建proise,并执行。所以interceptor的原理是在发动申请前做一些列操作,而后发动申请,最初执行返回后的操作。很天然会想到promise操作 promise = Promise.resolve(config); while (chain.length) { promise = promise.then(chain.shift(), chain.shift()); } return promise;}// request的interceptor以同步模式执行var newConfig = config;while (requestInterceptorChain.length) { var onFulfilled = requestInterceptorChain.shift(); var onRejected = requestInterceptorChain.shift(); try { // 同步执行onFulfilled办法 newConfig = onFulfilled(newConfig); } catch(error) { // 同步执行onRejected办法 onRejected(error); break; }}// 异步发动申请try { promise = dispatchRequest(newConfig);} catch(error) { return Promise.reject(error);}// 异步执行response的interceptorwhile (responseInterceptorChain.length) { promise = promise.then(responseInterceptorChain.shift(), responseInterceptorChain.shift());}return promise;};
小结一下,Axios.js这段操作,次要做了一下这些工作:
- 过滤request的interceptor:通过forEach办法、runWhen参数
- 决定request的interceptor是同步执行还是异步执行:因为批处理,要么都是同步执行,要么都是异步执行
- 把符合要求request interceptor推入requestInterceptorChain
- 过滤response的interceptor:通过forEach办法
- 把符合要求response interceptor推入responseInterceptorChain
- 如果request的interceptor是异步执行,那么合并操作:request interceptor、发动申请、response interceptor,用promise.then操作来生产这些操作
- 如果request的interceptor是同步执行,那么同步执行request的interceptor,而后用promise.then异步发动申请、异步解决response interceptor
- 返回promise
值得借鉴的中央
- InterceptorManager的实现中,eject办法,是间接置null,而不是删除元素。如果须要无效的元素,则再写一个过滤办法来获取。这样的益处的不会扭转前面元素的index。
- 结构[onFullfilled, onRejected]数据格式,巧用promise.then来生产一系列操作。
总结
- interceptor自身就是一个收集解决的对象
- axios是通过合并前置操作、发动申请、后置操作,而后用promise.then来生产一些列操作。