简体   繁体   English

如何编写超时的异步(基于承诺)function?

[英]How to write an asynchronous (promise-based) function that times out?

I want to have function which will do some work, if then is some condition true, then resolve promise.我想要 function 可以做一些工作,如果某些条件为真,则解决 promise。 If not, wait little bit (lets say one second) and then try it again.如果没有,请稍等(让我们说一秒钟)然后再试一次。 Beside that if time limit expired, promise will reject.除此之外,如果时间限制到期,promise 将拒绝。 How can do I that?我怎么能那样做? Look at my code example...If I wrap entire function in return new Promise(), I cant use await inside of it (which I need...I need to sleep some time if condition fail, and also I need wait to my await at end of my function).看看我的代码示例...如果我包装整个 function 以返回 new Promise(),我不能在其中使用 await(我需要...如果条件失败,我需要睡觉一段时间,而且我需要等待我在函数结束时等待)。

Here is my example of code:这是我的代码示例:

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
}

NOTE: My biggest problem is - how can I write this function to be able to catch rejection (at place where I call funcWithTimeLimit() ) in case of time limit expiration?注意:我最大的问题是 - 我如何编写这个 function 以便能够在时间限制到期的情况下捕获拒绝(在我调用funcWithTimeLimit()的地方)?

There are two basic approaches.有两种基本方法。 One is to repeatedly check the time and throw an exception once you hit the limit, the other is to race each await ed promise against a timeout .一种是重复检查时间并在达到限制后抛出异常,另一种是每个await ed promise 与 timeout 竞争 A third would require cooperation by the asynchronous tasks that you start, if they give you a way to cancel them in their interface, to simply offload the task of checking for timeout to them.第三种将需要您启动的异步任务的合作,如果它们为您提供一种在其界面中取消它们的方法,则只需将检查超时的任务卸载给它们。

In neither of them you can reject the async function by throwing an exception from a setTimeout .在它们中,您都不能通过从setTimeout抛出异常来拒绝async function

  1. Rather simple:相当简单:

     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 ""; }
  2. Racing:赛车:

     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 ""; }

    Notice that the setTimeout keeps the event loop open if when the function already completed, we just ignore the rejected promise.请注意,如果当 function 已经完成时, setTimeout会保持事件循环打开,我们只是忽略拒绝的 promise。 Better, but more laborious, is to cancel the timeout when we don't need it any longer:更好但更费力的是在我们不再需要超时时取消它:

     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); // ^^^^^^^^^^^^^^^^^^^^ } }
  3. With cooperation from the called function it's better of course:与被称为 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 ""; }

You can (where applicable) and should (where sensible) combine these approaches of course.当然,您可以(在适用的情况下)并且应该(在合理的情况下)结合这些方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM