axios较完美的超时以及重试失败解决方案 作者: LiesAuer 时间: 2020-01-06 分类: 开发 ## 代码引用 1. [Retry Solution From KyleRoss@github](https://github.com/axios/axios/issues/164#issuecomment-327837467 "Retry Solution From KyleRoss@github") 2. [Timeout Solution From aarcangeli@github](https://github.com/axios/axios/issues/2143#issuecomment-495994940 "Timeout Solution From aarcangeli@github") ## axios 的 retry 解决方案 见:[Retry Solution From KyleRoss@github](https://github.com/axios/axios/issues/164#issuecomment-327837467 "Retry Solution From KyleRoss@github") ## axios 的 timeout 问题 在`0.18.0`版本中`axios`的`timeout`并没有被正确实现导致有可能在弱网环境或不稳定环境或代理环境下`timeout`无效、程序卡住的情况(见:[Axios will not timeout on idle sockets](https://github.com/axios/axios/issues/2143 "Axios will not timeout on idle sockets")),虽然这问题在`0.19.0`版本中修复了,但是`0.19.0`版本中对配置合并的规则的阻断性更新导致我们的重试失败解决方案失效(见:[Axios 0.19.0 issue](https://github.com/axios/axios/issues/2397 "Axios 0.19.0 issue")),所以我们无法升级到`0.19.0`来解决这个问题。 ## 用 CancelToken 替代 timeout 根据 [@aarcangeli](https://github.com/aarcangeli "@aarcangeli") 提供的 [CancelToken 代替方案](https://github.com/axios/axios/issues/2143#issuecomment-495994940 "CancelToken 代替方案"),我们可以很轻松的解决上面所述的 timeout 问题。 ```js const CancelToken = axios.CancelToken; const source = CancelToken.source(); let timeout = setTimeout(() => source.cancel('Timeout'), 5000); axios.get('http://localhost:3000', { timeout: 1000, cancelToken: source.token }) .then(() => { clearTimeout(timeout); console.log('hello there') }); ``` ## 最终解决方案 ```js const axios = require('axios'); timeout = 10000; // BUG: https://github.com/axios/axios/issues/2143 // axios.defaults.timeout = timeout; axios.defaults.retry = 3; axios.defaults.retryDelay = 1000; function axiosRetryInterceptor(err) { var message, config; if (axios.isCancel(err)) { message = err.message.message; config = err.message.config; } else { message = err.message; config = err.config; } config.clearCancelToken(); // If config does not exist or the retry option is not set, reject if(!config || !config.retry) return Promise.reject(new Error(message)); // Set the variable for keeping track of the retry count config.__retryCount = config.__retryCount || 0; // Check if we've maxed out the total number of retries if(config.__retryCount >= config.retry) { // Reject with the error return Promise.reject(new Error(message)); } // Increase the retry count config.__retryCount += 1; // Create new promise to handle exponential backoff var backoff = new Promise(function(resolve) { setTimeout(function() { resolve(); }, config.retryDelay || 1); }); // Return the promise in which recalls axios to retry the request return backoff.then(function() { console.log(`请求失败,重试中:${config.url}`); return axios(config); }); } axios.interceptors.request.use(function (config) { const CancelToken = axios.CancelToken; const source = CancelToken.source(); let token = setTimeout(() => source.cancel({message:'Timeout', config: config}), timeout); config.cancelToken = source.token; config.clearCancelToken = () => clearTimeout(token); return config; }); axios.interceptors.response.use(function (response){ response.config.clearCancelToken(); return response; }, axiosRetryInterceptor); axios.get("https://example.com/").then(response => { console.log(response.data); }, error => console.log(error)); ``` ## 注意事项 `npm install`时请指定`axios`版本为`0.18.0`,因为上面的代码针对的都是`0.18.0`版本的`axios`。 ```shell npm install axios@0.18.0 --save ``` 标签: none
232