Axios 拦截器的Promise链式调用设计

184 阅读2分钟

Axios Interceptor - Promise Chaining

日常用烂的工具居然也能看出点新玩意

Axios Interceptor Example

在创建 axios 实例时,axios 会初始化一个名为 interceptors 的对象,其中包含了两个属性:requestresponse。这两个属性分别对应了一个数组,用于存储请求拦截器和响应拦截器。当我们添加一个拦截器时,实际上就是将该拦截器的回调函数放入对应数组的末尾。

import axios from "axios";

// 创建 axios 实例
const instance = axios.create({
  baseURL: "<https://api.example.com>",
  timeout: 10000,
});

// 添加请求拦截器
instance.interceptors.request.use(requestHandler, errorHandler);

// 添加响应拦截器
instance.interceptors.response.use(responseHandler, errorHandler);

Axios Request Dispatcher

function dispatchRequest(config) {
  // 默认的处理函数
  // 此处故意放置了两个元素
  // 第一个元素代表 axios 实际请求的 fulfill
  // 第二个元素代表 axios 实际请求的 reject, 这里设置为 undefined, 代表不处理
  var chain = [dispatchRequestAjax, undefined];

  // 创建包含config的Promise对象
  var promise = Promise.resolve(config);

  // 将所有请求拦截器的回调函数添加到chain数组的最前面
  this.interceptors.request.forEach(function unshiftRequestInterceptors(
    interceptor
  ) {
    chain.unshift(interceptor.fulfilled, interceptor.rejected);
  });

  // 将所有响应拦截器的回调函数添加到chain数组的最后面
  this.interceptors.response.forEach(function pushResponseInterceptors(
    interceptor
  ) {
    chain.push(interceptor.fulfilled, interceptor.rejected);
  });
  
  /* 
		如果同时存在请求拦截器和响应拦截器
    那么最终构成的chain数组的结构如下
		注意顺序
    [
      ...(multiple request interceptors),                                 
      requestFullfil_2, requestReject_2,
      requestFullfil_1, requestReject_1,
      dispatchRequestAjax, undefined,      // 一开始默认添加的处理函数
      responseFullfil_1, responseReject_1,
      responseFullfil_2, responseReject_2,
      ...(multiple response interceptors)
    ]
  */

  // 通过reduce方法将所有回调函数串联起来执行
  while (chain.length) {
    promise = promise.then(chain.shift(), chain.shift());
  }

  // 返回处理后的结果
  return promise;
}

这一段最经典的地方在于:

  • 其中最经典的地方在于 while (chain.length) 这一行代码,通过循环取出 chain 数组中的回调函数,实现了链式调用的本质。

  • 这段代码实现了 promise 的链式调用。

    • Promise.then 方法接受两个参数,分别是 onFulfilledonRejected
    • 每次取出两个元素(代表成功回调和失败回调)作为 then 方法的参数,然后将返回的 Promise 对象赋值给 promise,最终返回处理后的 promise 对象。
  • 这样做的好处是可以将多个回调函数按照一定的顺序执行,实现拦截器的链式调用,方便我们对请求和响应进行处理和管理。

  • 假设有如下数个拦截器:

     [
          requestFullfil_2, requestReject_2,
          requestFullfil_1, requestReject_1,
          dispatchRequestAjax, undefined,      // 一开始默认添加的处理函数
          responseFullfil_1, responseReject_1,
          responseFullfil_2, responseReject_2,
       ]
    
    • 其中如果完全resolve, 则执行顺序为:

      1. requestFullfil_2
      2. requestFullfil_1
      3. dispatchRequestAjax
      4. responseFullfil_1
      5. responseFullfil_2
    • 假设 requestFullfil_2 reject, 则执行顺序为:

      1. requestFullfil_2(fail) + requestReject_2
      2. requestReject_1
      3. undefined, 等于当前 promise 没有 onReject事件, 继续 reject
      4. requestReject_1
      5. requestReject_2