我在網上看到不少關於fetch timeout的封裝,可是我以爲是僞timeout,只是拋錯,可是fetch的Promise鏈會一直執行下去git
Promise.race([
fetch('/api')
.then(res => res.json())
.then(res => console.log(555)),
new Promise(function(resolve, reject) {
setTimeout(() => {
reject(new Error('request timeout'));
console.log(111);
}, 100);
})
]);
複製代碼
結果: github
能夠看到就算超時後,fetch請求仍按正常順序執行,輸出了555,超時通常會從新請求,這樣到最後就有可能輸出2次或者屢次555,試想若是你在then函數裏面執行setState操做,這樣視圖就會更新2次或者屢次,這樣顯然不是咱們想要的結果,咱們想要的是獲取結果後執行一次因而我封裝如下代碼,支持timeout(我這個其實也是僞timeout,沒辦法,除非使用xhr,可是超時後Promise鏈只會執行報錯,由於結果和報錯使用同一個Promise)和從新請求,因爲返回值是一個Promise,用法和fetch保持一致 支持Promise.all,.race
方法json
代碼地址api
class TimeoutError extends Error {
constructor(message) {
super(message);
this.name = 'TimeoutError';
}
}
/** * 提供參數校驗和wrapper功能 * * @param {*} url * @param {*} [options={ method: 'GET' }] * @returns {Promise} the request result */
function request(url, options = { method: 'GET' }) {
let retryCount = 0;
let parseJSON = response => {
return response.json();
};
let checkStatus = response => {
if (response.status >= 200 && response.status < 300) {
return response;
}
let error = new Error(response.statusText);
error.response = response;
throw error;
};
class Request {
constructor(url, { retry, timeout, ...options }) {
this.url = url;
this.retry = retry || 0;
this.timeout = timeout || 10000;
this.options = options;
}
then(fn) {
let done = false;
setTimeout(() => {
// 不管是請求重試仍是最終超時錯誤,這次請求獲得的結果做廢
if (retryCount < this.retry && !done) {
done = true;
retryCount++;
this.then(fn);
} else {
let error = new TimeoutError(`timeout of ${this.timeout}ms execeeded`);
this.catchError(error);
}
}, this.timeout);
fetch(this.url, this.options)
.then(checkStatus)
.then(parseJSON)
.then(res => {
// 未進入重試或者超時錯誤,返回結果
if (!done) {
fn(res);
done = true;
}
})
.catch(err => {
this.catchError(err);
});
return this;
}
catch(fn) {
this.catchError = fn;
}
}
return new Promise((resolve, reject) =>
new Request(url, options).then(res => resolve(res)).catch(err => reject(err))
);
}
request('/api', {
retry: 2,
timeout: 1000
}).then(res => console.log(res))
複製代碼
設置Cache-Control:2s和timeout:1000ms後的請求狀況 能夠看到1.49s後請求才徹底響應,而咱們設置了1s從新請求,因此第二次請求因爲上次請求緩存未失效的緣由,在1.49s的時候利用了上次請求做爲結果進行了響應 設置緩存,第一次超時請求結果做廢(then函數不執行),第二次請求直接拿了第一次的緩存,這樣減小了請求響應時間還減輕了服務器的壓力 緩存
請求重試最好跟cache-control配合使用,這樣當前面請求超時結果做廢后,第二次請求會等到第一次請求結果的返回,前提是緩存沒有失效 緩存失效時間是從響應開始時計算的,通常配合超時從新請求的話,timeout設置爲正常響應的1.5倍,max-age應該設置爲timeout的1.5+倍(或者爲timeout的2倍,方便利用上次響應結果),具體數值須要根據具體狀況合理設置服務器
可能最後會有人有這樣的疑問,你使用緩存,即上一次請求超時響應的結果,那還不如Promise.race的方法簡單,同樣的效果 使用緩存的優點就是若是第一次超時響應的時間短於timeout加正常響應甚至又一次超時的時間,並且緩存沒有失效,那麼既節省了時間又節省了服務器的壓力,假如失效了呢?從新請求唄!無論怎樣,利用緩存絕對是比不利用的好網絡
最後,若是你以爲這篇文章對你有用的話,麻煩給個小星星,若有錯誤的話,也歡迎指正app