网络编程
位置:首页>> 网络编程>> JavaScript>> axios * 工作方式及原理源码解析

axios * 工作方式及原理源码解析

作者:锋利的绵羊  发布时间:2023-07-02 16:38:36 

标签:axios, , ,工作原理

axios * 的配置方式

本文所用 axios 版本号为:1.3.2

axios 中有两种 * :

  • axios.interceptors.request.use(onFulfilled, onRejected, options):配置请求 * 。

    • synchronous:控制请求 * 是否为异步执行,默认为 true,每个 * 都可以单独设置,只要有一个设置为 false 则为同步执行,否则为异步执行。

    • runWhen:一个方法,接收 config 对象作为参数,在每次调用设定的 * 方法前调用,返回结果为 true 时执行 * 方法。

    • onFulfilled 方法在发送请求前执行,接收 config 对象,返回一个新的 config 对象,可在此方法内修改 config 对象。

    • onRejected 方法在 onFulfilled 执行错误后执行,接收 onFulfilled 执行后的错误对象。

    • options 配置参数

    • 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.requestinterceptors.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

    同步执行请求 * (顺序执行)

    synchronousRequestInterceptorsfalse 时为同步执行,相关逻辑如下:

    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 时默认为异步执行请求 * 模式,即遍历执行所有的请求 * 一参回调,执行报错后停止遍历,并执行该 * 的二参回调。

    • 同步执行请求 * 模式。

    • 设置 synchronousfalse 时为同步执行请求 * 模式,将执行链包装成一个 Promise 链顺序执行。

    * 的执行顺序

    先执行请求 * ,后执行响应 * 。

    • 设置多个请求 * 的情况:后添加的 * 先执行。

    • 设置多个响应 * 的情况:按照设置顺序执行。

    同步&异步

    计算机中的同步,指的是现实中的一步一步(同一时间只能干一件事),异步指的是同时进行(同一时间能干多件事)。

    参考 npm axios

    来源:https://juejin.cn/post/7198326957108838455

    0
    投稿

    猜你喜欢

    手机版 网络编程 asp之家 www.aspxhome.com