问题描述
我想要一个可以做一些工作的函数,如果某些条件为真,则解决承诺。如果没有,请稍等(让我们说一秒钟),然后再试一次。除此之外,如果时间限制到期,promise 将拒绝。我怎么能这样?看看我的代码示例...如果我将整个函数包装在 return 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
}
注意:我最大的问题是 - 我如何编写这个函数以便在时间限制到期时能够捕获拒绝(在我调用 funcWithTimeLimit() 的地方)?
解决方法
有两种基本方法。一种是反复检查时间,一旦达到限制就抛出异常,另一种是race each await
ed promise against a timeout。第三个将需要您启动的异步任务的合作,如果它们让您可以在其界面中取消它们,只需将检查超时的任务卸载给它们。
在这两种情况下,您都不能通过从 async function
抛出异常来拒绝 setTimeout
。
-
相当简单:
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 ""; }
注意
setTimeout
保持事件循环打开,如果函数已经完成,我们只是忽略被拒绝的承诺。更好,但更费力的是,当我们不再需要时取消超时: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 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 ""; }
您当然可以(在适用的情况下)并且应该(在合理的情况下)将这些方法结合起来。