背景

某一时刻,测试环境的接口请求特别慢,导致接口请求超时,并且测试环境是必现的,当时axios的超时时间设置的是8s,因为我们关注这8s是接口开始请求到请求结束的时间,感觉怎么也够了,其实不然。同一个接口,在项目中请求,根据network来看,确实超过了8s,但是我们单独把这个接口用浏览器打开,却不到1s,这是怎么回事?下图算是一个例子,因为现在已经复现不了当时的情况了。。。
image.png

原因

拿我们最常用的Chrome浏览器来说,它正常能够同时进行6个TCP连接,也就是能够并发进行6个请求,而我们当时超时的接口在那6个之外,并且是进入页面就进行的请求,所以它一进入页面就开发发起,然后中途需要等着前6个请求完成,所以它的状态就算是一个阻塞,这种情况下,我们能看到network的waterfall里面,stalled占用时间特别长,可能接口拿回来数据只需要1s,但是它在stalled状态就待了7s,导致接口超时,这种情况下,如果我们重新发起这单个请求,就会在1s内马上拿到数据,因为前面6个接口造成的阻塞状态也差不多过去了
image.png

解决

服务器接口慢、网络慢,这种是前端无法避免会出现的偶发情况,所以我们需要对超时的接口进行处理,我们给接口3次机会,排除因为浏览器并发限制而出现的阻塞情况,所以我们可以给接口设置重新请求

  1. //在main.js设置全局的请求次数,请求的间隙
  2. const retry = 3;
  3. const retryDelay = 1000;
  4. axios.interceptors.response.use(response => {
  5. // 相关相应处理
  6. ...
  7. },error => {
  8. // Do something with response error
  9. if (error.code === 'ECONNABORTED' && error.message.indexOf('timeout') !== -1) {
  10. let config = error.config;
  11. // 没有config则reject
  12. if(!config || !retry) return Promise.reject(error);
  13. config.__retryCount = config.__retryCount || 0;
  14. // 判断是否已经超过重新请求次数
  15. if(config.__retryCount >= retry) {
  16. // 弹窗告诉用户,当前网络不好
  17. store.dispatch('net_timeout', true);
  18. return Promise.reject(error);
  19. }
  20. config.__retryCount += 1;
  21. // 设置重新请求promise
  22. let backOff = new Promise(function(resolve) {
  23. setTimeout(function() {
  24. resolve();
  25. }, retryDelay || 1);
  26. });
  27. return backOff.then(function() {
  28. return axios(config);
  29. });
  30. } else {
  31. store.dispatch('net_timeout', false)
  32. console.warn('axios response error')
  33. return Promise.reject(error)
  34. }
  35. })

注意:
网上有很多解决方案里面,都将重新请求次数和延迟时间设置到axios的defaults对象中,但是这种设置方式是有限制的,必须在axios版本在0.18.0才生效

  1. axios.defaults.retry = 3;
  2. axios.defaults.retryDelay = 1000;