[英]How to write an asynchronous (promise-based) function that times out?
我想要 function 可以做一些工作,如果某些條件為真,則解決 promise。 如果沒有,請稍等(讓我們說一秒鍾)然后再試一次。 除此之外,如果時間限制到期,promise 將拒絕。 我怎么能那樣做? 看看我的代碼示例...如果我包裝整個 function 以返回 new Promise(),我不能在其中使用 await(我需要...如果條件失敗,我需要睡覺一段時間,而且我需要等待我在函數結束時等待)。
這是我的代碼示例:
async funcWithTimeLimit(numberToDecrement, timeLimit){
let sleep = undefined;
let timeLimitTimeout = setTimeout(() => {
if (sleep)
clearTimeout(sleep);
return Promise.reject("Time limit of " + (timeLimit/1000) +" secs expired"); //reject
}, timeLimit);
while(numberToDecrement > 0){
for(let i = 10;(i > 0 && numberToDecrement > 0); i--){ //Do some work
numberToDecrement--;
}
if(numberToDecrement > 0){
await new Promise(resolve => sleep = setTimeout(resolve, 1000));
}
}
clearTimeout(timeLimitTimeout);
await new Promise((resolve, reject) => sleep = setTimeout(resolve, 500)); // Do something
return ""; //resolve
}
注意:我最大的問題是 - 我如何編寫這個 function 以便能夠在時間限制到期的情況下捕獲拒絕(在我調用funcWithTimeLimit()的地方)?
有兩種基本方法。 一種是重復檢查時間並在達到限制后拋出異常,另一種是每個await
ed promise 與 timeout 競爭。 第三種將需要您啟動的異步任務的合作,如果它們為您提供一種在其界面中取消它們的方法,則只需將檢查超時的任務卸載給它們。
在它們中,您都不能通過從setTimeout
拋出異常來拒絕async function
。
相當簡單:
function doSomething() { return new Promise((resolve, reject) => { setTimeout(resolve, 500)); // Do something } } async funcWithTimeLimit(numberToDecrement, timeLimit) { while (numberToDecrement > 0) { for (let i = 10; i > 0 && numberToDecrement > 0; i--) { if (Date.now() > timelimit) throw new TimeoutError("Exceeded limit"); // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ numberToDecrement--; // Do some work } if (numberToDecrement > 0) { if (Date.now() + 1000 > timelimit) throw new TimeoutError("Exceeded limit"); // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ await new Promise(resolve => setTimeout(resolve, 1000)); } } // but ineffective here: await doSomething(); return ""; }
賽車:
async funcWithTimeLimit(numberToDecrement, timeLimit) { const timeout = new Promise((resolve, reject) => { setTimeout(() => { reject(new TimeoutError("Exceeded limit")); }, timeLimit - Date.now()); }); timeout.catch(e => void e); // avoid unhandled promise rejection if not needed while (numberToDecrement > 0) { for (let i = 10; i > 0 && numberToDecrement > 0; i--) { // no guarding of synchronous loops numberToDecrement--; // Do some work } if (numberToDecrement > 0) { await Promise.race([ timeout, // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ new Promise(resolve => setTimeout(resolve, 1000)), ]); } } await Promise.race([ timeout, // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doSomething(), ]); return ""; }
請注意,如果當 function 已經完成時, setTimeout
會保持事件循環打開,我們只是忽略拒絕的 promise。 更好但更費力的是在我們不再需要超時時取消它:
async funcWithTimeLimit(numberToDecrement, timeLimit) { let timer; const timeout = new Promise((resolve, reject) => { timer = setTimeout(() => { reject(new TimeoutError("Exceeded limit")); }, timeLimit - Date.now()); }); try { // as before: while (numberToDecrement > 0) { for (let i = 10; i > 0 && numberToDecrement > 0; i--) { numberToDecrement--; // Do some work } if (numberToDecrement > 0) { await Promise.race([ timeout, new Promise(resolve => setTimeout(resolve, 1000)) ]); } } await Promise.race([ timeout, doSomething() ]); return ""; } finally { clearTimeout(timer); // ^^^^^^^^^^^^^^^^^^^^ } }
與被稱為 function 的合作當然更好:
function delay(t, limit) { if (Date.now() + t > timelimit) throw new TimeoutError("Exceeded limit"); return new Promise(resolve => setTimeout(resolve, t)); } function doSomething(limit) { return new Promise((resolve, reject) => { const timeout = setTimeout(() => { xhr.cancel(); // custom API-dependent cancellation reject(new TimeoutError("Exceeded limit")); }, limit - Date.now()); const xhr = someApiCall(); // Do something xhr.onerror = err => { clearTimeout(timeout); reject(err); }; xhr.onload = res => { clearTimeout(timeout); resolve(res); }; } } async funcWithTimeLimit(numberToDecrement, timeLimit) { while (numberToDecrement > 0) { for (let i = 10; i > 0 && numberToDecrement > 0; i--) { numberToDecrement--; // Do some work } if (numberToDecrement > 0) { await delay(1000, timeLimit); // ^^^^^^^^^ } } await doSomething(timeLimit); // ^^^^^^^^^ return ""; }
當然,您可以(在適用的情況下)並且應該(在合理的情況下)結合這些方法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.