1

接口分析

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
    企业微信截图_16294485158073.png
  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.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来消费一些列操作。

RockerLau
363 声望11 粉丝

Rocker Lau