美文网首页程序员
使用axios在request拦截器中取消请求

使用axios在request拦截器中取消请求

作者: sunshineLWZL | 来源:发表于2018-09-13 10:28 被阅读0次

    最近在工作中遇到这样的需求,新用户在第一次登录的时候需要强制修改密码。刚开始接到这个需求的时候,心里想着在axios的请求拦截器中统一处理就可以了,判断用户是否需要修改密码,如果需要修改密码就返回一个reject,从而不发送请求。好像很简单啊,说干就干。
    首先照着上面的想法,撸出下列代码:

    http.interceptors.request.use(config => {
      if (needChangePassword) {
        return Promise.reject(new Error('changePassword'))
      } else {
        config.cancelToken = store.source.token
        return config
      }
    }, err => {
      return Promise.reject(err)
    })
    

    心里想着,如果需要修改密码,就调用reject,然后在每个发送请求的外面都会有catch异常,在catch中统一处理错误信息,so beautiful,保存->运行,唉唉唉,怎么reject之后跑到外面的then里面去了,不是应该跳catch吗?有毒啊。
    各种debugger之后,发现无论怎么reject,就是不跳catch。在走投无路之下,只能求助源码了,最后在源码core->Axios.js中发现了promise的调用顺序,主要源码如下:

      var chain = [dispatchRequest, undefined];
      var promise = Promise.resolve(config);
    
      this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
        chain.unshift(interceptor.fulfilled, interceptor.rejected);
      });
    
      this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
        chain.push(interceptor.fulfilled, interceptor.rejected);
      });
    
      while (chain.length) {
        promise = promise.then(chain.shift(), chain.shift());
      }
    
    

    看完之后,才不得不感叹下,大牛果然就是不一样。源码的大致意思是这样的,首先处理request拦截器相关的promise,然后再发送请求,然后再处理respose拦截器相关promise,通过一个while循环生成了一个promsie链,最后返回的promise相当于下面:

    promise = promise.then(request.fuifilled1, request.reject1)
    .then(request.fuifilled2, request.reject)...
    .then(dispatchRequest, undefined)
    .then(response.fuifilled1, response.reject1)
    .then(response.fuifilled2, response.reject2)...
    .then(response.fuifilledn, response.rejectn)
    

    这个chain真是用的好,又学习到了。最后回到上面的问题,应该就很简单了,在request的fuifilled中reject,然后到undefined,然后到response中的reject。由于最开始没有在response的reject处理changePassword的错误,并且没有在reject中始终返回reject,所有就跑到外面的then里面,而没有跳到catch中。
    最后附上修改后的代码:

    http.interceptors.request.use(config => {
      if (needChangePassword) {
        return Promise.reject(new Error('changePassword'))
      } else {
        config.cancelToken = store.source.token
        return config
      }
    }, err => {
      return Promise.reject(err)
    })
    // 配置拦截器与错误处理
    http.interceptors.response.use(function (response) {
      const code = _.get(response, 'data.code')
      if (code !== 10000) {
        if (code === 10008) {
          window.location.href = '/'
        } else {
          isDev && console.error(response, '请求返回状态码不为10000')
          throw new Error(response.data.message)
        }
      } else {
        return response.data.data ? response.data.data : response.data
      }
    }, function (error) {
      if (axios.isCancel(error)) {
        throw new Error('cancelled')
      } else if (error.message === 'changePassword') {
        throw new Error('changePassword')
      } else {
        if (error.response) {
          if (isDev) {
            console.group('请求发生错误')
            console.log(error.response.data)
            console.log(error.response.status)
            console.log(error.response.headers)
            console.groupEnd()
          }
          throw new Error('接口异常, 请联系管理员')
        }
      }
    })
    

    最后感叹下,源码还是个好东西,既能加深理解,又能学习知识。

    相关文章

      网友评论

        本文标题:使用axios在request拦截器中取消请求

        本文链接:https://www.haomeiwen.com/subject/pdoigftx.html