node.js 异步/等待超时
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/37120240/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Timeout in async/await
提问by nkint
I'm with Node.js and TypeScript and I'm using async/await.
This is my test case:
我使用 Node.js 和 TypeScript,我正在使用async/await. 这是我的测试用例:
async function doSomethingInSeries() {
const res1 = await callApi();
const res2 = await persistInDB(res1);
const res3 = await doHeavyComputation(res1);
return 'simle';
}
I'd like to set a timeout for the overall function. I.e. if res1takes 2 seconds, res2takes 0.5 seconds, res3takes 5 seconds I'd like to have a timeout that after 3 seconds let me throw an error.
我想为整个功能设置一个超时。即如果res1需要 2 秒,res2需要 0.5 秒,res3需要 5 秒我想有一个超时,在 3 秒后让我抛出一个错误。
With a normal setTimeoutcall is a problem because the scope is lost:
正常setTimeout调用是一个问题,因为范围丢失:
async function doSomethingInSeries() {
const timerId = setTimeout(function() {
throw new Error('timeout');
});
const res1 = await callApi();
const res2 = await persistInDB(res1);
const res3 = await doHeavyComputation(res1);
clearTimeout(timerId);
return 'simle';
}
And I cannot catch it with normal Promise.catch:
我无法用 normal 捕捉它Promise.catch:
doSomethingInSeries().catch(function(err) {
// errors in res1, res2, res3 will be catched here
// but the setTimeout thing is not!!
});
Any ideas on how to resolve?
关于如何解决的任何想法?
回答by Bergi
You can use Promise.raceto make a timeout:
您可以使用Promise.race超时:
Promise.race([
doSomethingInSeries(),
new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), 11.5e3))
]).catch(function(err) {
// errors in res1, res2, res3 and the timeout will be caught here
})
You cannot use setTimeoutwithout wrapping it in a promise.
如果setTimeout不将其包装在 Promise 中,则无法使用。
回答by nkint
Ok I found this way:
好的,我找到了这种方式:
async function _doSomethingInSeries() {
const res1 = await callApi();
const res2 = await persistInDB(res1);
const res3 = await doHeavyComputation(res1);
return 'simle';
}
async function doSomethingInSeries(): Promise<any> {
let timeoutId;
const delay = new Promise(function(resolve, reject){
timeoutId = setTimeout(function(){
reject(new Error('timeout'));
}, 1000);
});
// overall timeout
return Promise.race([delay, _doSomethingInSeries()])
.then( (res) => {
clearTimeout(timeoutId);
return res;
});
}
Anyone errors?
任何人的错误?
The things that smells a bit to me is that using Promises as asynchronicity strategy will send us to allocate too many object that some other strategy needs but this is off-topic.
对我来说有点味道的事情是,使用 Promises 作为异步策略会让我们分配太多其他策略需要的对象,但这是题外话。
回答by Alexander Danilov
Problem with @Bergi answer that doSomethingInSeriescontinues executing even if you already rejected the promise. It is much better to just cancel by timeout.
doSomethingInSeries即使您已经拒绝了承诺,@Bergi 答案仍会继续执行的问题。最好通过超时取消。
Here is support of cancellation:
这是取消的支持:
async function doSomethingInSeries(cancellationToken) {
cancellationToken.throwIfCancelled();
const res1 = await callApi();
cancellationToken.throwIfCancelled();
const res2 = await persistInDB(res1);
cancellationToken.throwIfCancelled();
const res3 = await doHeavyComputation(res1);
cancellationToken.throwIfCancelled();
return 'simle';
}
But it is even better to pass cancellation token to each async function and use it there.
但是最好将取消令牌传递给每个异步函数并在那里使用它。
Here is implementation of cancellation:
这是取消的实现:
let cancellationToken = {
cancelled: false,
cancel: function() {
this.cancelled = true;
},
throwIfCancelled: function() {
if (this.cancelled) throw new Error('Cancelled');
}
}
You can wrap it as class if you want.
如果需要,您可以将其包装为类。
And finally usage:
最后用法:
doSomethingInSeries(cancellationToken);
setTimeout(cancellationToken.cancel, 5000);
Keep in mind that task is not cancelled immediately, so continuation (awaiting, then or catch) is not called exactly after 5 secs. To guarantee that you can combine this and @Bergi approach.
请记住,任务不会立即取消,因此不会在 5 秒后恰好调用继续(等待、然后或捕获)。为了保证您可以将此方法与@Bergi 方法结合使用。

