axios如何实现interceptors

接口分析
https://github.com/axios/axio...
普通用法

// Add a request interceptor axios.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 interceptor axios.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引擎会先执行同步代码,执行完了,会去查看任务队列,看看有没有异步任务的结果,有的话推送到执行栈执行。那我们下面看看它是如何实现同步执行的。
源码分析
  1. 提取关键字interceptors,定位到Axios.jsInterceptorManager.js
    axios如何实现interceptors
    文章图片
  2. 查看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的功能
  3. 再看Axios.js
    line18~21:初始化intercepor
    this.interceptors = { request: new InterceptorManager(), response: new InterceptorManager() };

    line60~118: 过滤interceptors,并合并处理
    var requestInterceptorChain = []; var synchronousRequestInterceptors = true; // 用InterceptorManager的forEach方法,已经过滤为null的interceptor this.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的interceptor while (responseInterceptorChain.length) { promise = promise.then(responseInterceptorChain.shift(), responseInterceptorChain.shift()); } return promise; };

    【axios如何实现interceptors】小结一下,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
值得借鉴的地方
  1. InterceptorManager的实现中,eject方法,是直接置null,而不是删除元素。如果需要有效的元素,则再写一个过滤方法来获取。这样的好处的不会改变后面元素的index。
  2. 构造[onFullfilled, onRejected]数据格式,巧用promise.then来消费一系列操作。
总结
  1. interceptor本身就是一个收集处理的对象
  2. axios是通过合并前置操作、发起请求、后置操作,然后用promise.then来消费一些列操作。

    推荐阅读