背景
某一时刻,测试环境的接口请求特别慢,导致接口请求超时,并且测试环境是必现的,当时axios的超时时间设置的是8s,因为我们关注这8s是接口开始请求到请求结束的时间,感觉怎么也够了,其实不然。同一个接口,在项目中请求,根据network来看,确实超过了8s,但是我们单独把这个接口用浏览器打开,却不到1s,这是怎么回事?下图算是一个例子,因为现在已经复现不了当时的情况了。。。
原因
拿我们最常用的Chrome浏览器来说,它正常能够同时进行6个TCP连接,也就是能够并发进行6个请求,而我们当时超时的接口在那6个之外,并且是进入页面就进行的请求,所以它一进入页面就开发发起,然后中途需要等着前6个请求完成,所以它的状态就算是一个阻塞,这种情况下,我们能看到network的waterfall里面,stalled占用时间特别长,可能接口拿回来数据只需要1s,但是它在stalled状态就待了7s,导致接口超时,这种情况下,如果我们重新发起这单个请求,就会在1s内马上拿到数据,因为前面6个接口造成的阻塞状态也差不多过去了
解决
服务器接口慢、网络慢,这种是前端无法避免会出现的偶发情况,所以我们需要对超时的接口进行处理,我们给接口3次机会,排除因为浏览器并发限制而出现的阻塞情况,所以我们可以给接口设置重新请求
//在main.js设置全局的请求次数,请求的间隙
const retry = 3;
const retryDelay = 1000;
axios.interceptors.response.use(response => {
// 相关相应处理
...
},error => {
// Do something with response error
if (error.code === 'ECONNABORTED' && error.message.indexOf('timeout') !== -1) {
let config = error.config;
// 没有config则reject
if(!config || !retry) return Promise.reject(error);
config.__retryCount = config.__retryCount || 0;
// 判断是否已经超过重新请求次数
if(config.__retryCount >= retry) {
// 弹窗告诉用户,当前网络不好
store.dispatch('net_timeout', true);
return Promise.reject(error);
}
config.__retryCount += 1;
// 设置重新请求promise
let backOff = new Promise(function(resolve) {
setTimeout(function() {
resolve();
}, retryDelay || 1);
});
return backOff.then(function() {
return axios(config);
});
} else {
store.dispatch('net_timeout', false)
console.warn('axios response error')
return Promise.reject(error)
}
})
注意:
网上有很多解决方案里面,都将重新请求次数和延迟时间设置到axios的defaults对象中,但是这种设置方式是有限制的,必须在axios版本在0.18.0才生效
axios.defaults.retry = 3;
axios.defaults.retryDelay = 1000;