Axios 中的 拦截器 与 取消发送的简单模拟

551 阅读3分钟

看了下 axios 的源码,主要是 好奇 axios 中的 拦截器的实现与 取消发送的 机制
简单的实现了下,插个眼

axios拦截器官方

拦截器

    // 添加请求拦截器
    axios.interceptors.request.use(function interceptorsBefore(config) {
      // 在发送请求之前做些什么
      return config;
    }, function (error) {
      // 对请求错误做些什么
      return Promise.reject(error);
    });

    // 添加响应拦截器
    axios.interceptors.response.use(function interceptorsAfter(response) {
      // 2xx 范围内的状态码都会触发该函数。
      // 对响应数据做点什么
      return response;
    }, function (error) {
      // 超出 2xx 范围的状态码都会触发该函数。
      // 对响应错误做点什么
      return Promise.reject(error);
    });

拦截器主要分为 请求拦截器响应拦截器

在 源码的 Axios 文件下 request方法 62 行

  var requestInterceptorChain = [];
 
 this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {

    requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);
  });


  var responseInterceptorChain = [];
  this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
  });
  
  var promise;
  
 // dispatchRequest 是主要的 发送请求的方法
 var chain = [dispatchRequest, undefined];
 /*
 *
 */
 // 利用数组的Apply 方法传递数组的方法,把 请求拦截器数组插入到 chain 头部
 Array.prototype.unshift.apply(chain, requestInterceptorChain);
 
 // 把 响应拦截器数组插入到 chain 头部
 chain = chain.concat(responseInterceptorChain);

现在 chain 变成了

chain = [请求拦截成功,请求拦截失败,发送请求,undefined, 响应拦截成功,响应拦截失败]

现在开始执行,依旧在 request 方法里

 var promise = Promise.resolve(config);
while (chain.length) {
      promise = promise.then(chain.shift(), chain.shift());
 }
 return promise;

到达用户手中,只剩下一个 由最后一个响应拦截处理过后的 Promise

自己简单实现

由于axios 实例需要 axios.interceptors.request 这种写法

     const myAxios = function () {
      this.interceptors = {
        request: new Interceptors,
        response: new Interceptors,
      }
    }
    function Interceptors() {
      this.handlers = []
    }

Interceptors.prototype.use = function (fulfiled, rejected = () => { }) {
      this.handlers.push(
        fulfiled,
        rejected
      )
    }

重点: 生成自己的 Promise Chain dispathRequest 模拟真正请求的方法

 myAxios.prototype.request = function (config) {
      let p = Promise.resolve(config)
      let chain = [dispathRequest, undefined]
      
      Array.prototype.unshift.apply(chain, this.interceptors.request.handlers)
      Array.prototype.push.apply(chain, this.interceptors.response.handlers)

      while (chain.length) {
        p = p.then(chain.shift(), chain.shift())
      }
      return p
    }
    //
     function dispathRequest(config) {
      return new Promise((resolve, reject) => {
        resolve(config)
      })
    }

使用 拦截器

 let m = new myAxios

    m.interceptors.request.use(function success1(config) {
      console.log("successRequest");
      return config
    }, function error1(err) {
      return err
    })

    m.interceptors.response.use(function success2(config) {
      console.log("successResponse");
      return config

    }, function error2(err) {
      console.log("error");
      return err
    })

发送请求

let obj = { a: 20 }
m.request( obj ).then(res => {
      console.log(res, "res");
 })

obj 会随着 request 到达,请求拦截器,请求拦截器 return config

Promise 的机制就是上一个 then 中如果返回的 是个普通值(非Promise)类型,这个 返回的值就会被当做 下一个 then 的 value 值,如果是 Promise 类型,会解析 这哥 Pormise

dispathRequest 得到这个 config,继续执行, 响应拦截器 获取 数据 执行完毕,request最后 返回一个 promise

    m.request({ a: 20 }).then(res => {
      console.log(res, "res");
    })

取消请求

axios取消请求

   const CancelToken = axios.CancelToken;
   let cancel;
   axios.get('/user/12345', { cancelToken: new CancelToken(function executor(c) { 
    // executor 函数接收一个 cancel 函数作为参数 
        cancel = c;
       }) 
     });
    // 取消请求 
  cancel();

原理是巧妙的 Promise 的resolve 可以更改 Promise 状态 生成 cancelToken

let cancel;

function CancelToken(excutor) {
      let resolvePromise;
      
      this.promise = new Promise((resolve, reject) => {
        resolvePromise = resolve
      })
      
      excutor(function () {
        resolvePromise("cancelToken")
      })
    }
    
 let cancelToken = new CancelToken(function (c) {
      cancel = c
 })

首先生成 CancelToken 函数,传入 excutor(执行器),同时把 resolve 赋值给 外部变量 resolvePromise,这样的话,this.promise 的状态时 pending,把控制权 交给了 resolvePromise 也就是 下个函数中的 function (c){}

excutor(执行器) 中的 function () {resolvePromise("cancelToken")} 传递 参数 给 c,也就是说,现在 cancel 一旦执行,excutor中的 就会执行,resolvePromise 就会执行,this.promise 的状态就会发生改变

把 cancel 给axios 实例带上

m.request({ a: 20, cancelToken: cancel }).then(res => {
     console.log(res, "res");
 })
 
  function dispathRequest(config) {
     return new Promise((resolve, reject) => {
       if (config.cancelToken) {
         config.cancelToken()
         reject("取消请求")
       }
       resolve(config)
     })
   }
   
   cancelToken.promise.then(res => {
     console.log(res);
   }).catch(err => {
     console.log(err);
   })

全部代码 个人感觉还是很巧妙的

 const myAxios = function () {
     this.interceptors = {
       request: new Interceptors,
       response: new Interceptors,
     }
   }

   myAxios.prototype.request = function (config) {

     let p = Promise.resolve(config)
     let chain = [dispathRequest, undefined]

     Array.prototype.unshift.apply(chain, this.interceptors.request.handlers)
     Array.prototype.push.apply(chain, this.interceptors.response.handlers)


     while (chain.length) {
       p = p.then(chain.shift(), chain.shift())
     }
     return p
   }

   function dispathRequest(config) {
     return new Promise((resolve, reject) => {
       if (config.cancelToken) {
         config.cancelToken()
         reject("取消请求")
       }
       resolve(config)
     })
   }

   function Interceptors() {
     this.handlers = []
   }

   Interceptors.prototype.use = function (fulfiled, rejected = () => { }) {
     this.handlers.push(
       fulfiled,
       rejected
     )
   }

   let m = new myAxios

   m.interceptors.request.use(function success1(config) {
     console.log("successRequest");
     return config
   }, function error1(err) {
     return err
   })

   m.interceptors.response.use(function success2(config) {
     console.log("successResponse");
     return config

   }, function error2(err) {
     console.log("error");
     return err
   })


   let cancel;

   function CancelToken(excutor) {
     let resolvePromise;
     this.promise = new Promise((resolve, reject) => {
       resolvePromise = resolve
     })
     excutor(function () {
       resolvePromise("cancelToken")
     })
   }

   let cancelToken = new CancelToken(function (c) {
     cancel = c
   })

   cancelToken.promise.then(res => {
     console.log(res);
   }).catch(err => {
     console.log(err);
   })

   m.request({ a: 20, cancelToken: cancel }).then(res => {
     console.log(res, "res");
   })

note:time:2022/7/11