我使用的是Node.js和TypeScript,而我正在使用async/await
。
这是我的测试用例:
async function doSomethingInSeries() {
const res1 = await callApi();
const res2 = await persistInDB(res1);
const res3 = await doHeavyComputation(res1);
return 'simle';
}
我想为整个功能设置超时。即如果res1
需要2秒,res2
需要0.5秒,res3
需要5秒钟,我希望超时,3秒后让我抛出错误。
正常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';
}
我无法用正常的Promise.catch
来抓住它:
doSomethingInSeries().catch(function(err) {
// errors in res1, res2, res3 will be catched here
// but the setTimeout thing is not!!
});
有关如何解决的任何想法?
答案 0 :(得分:32)
您可以使用Promise.race
进行超时:
formDate
如果不将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
})
包含在承诺中,则无法使用{。}}。
答案 1 :(得分:4)
好的,我发现了这个方式:
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;
});
}
有人有错误吗?
对我来说有点气味的是,使用Promise作为异步策略会让我们分配太多其他策略需要的对象,但这是偏离主题的。
答案 2 :(得分:0)
@Bergi的问题回答COLUMNS
继续执行,即使您已经拒绝了诺言。最好只是通过超时取消。
这是取消的支持:
abstract class RepositoryBase<T> {
protected abstract readonly COLUMNS: Record<keyof T, string>;
strictColumns<
U extends { [K in keyof T | keyof U]: K extends keyof T ? string : never }
>(u: U) {
return u;
}
}
但是最好将取消令牌传递给每个异步函数并在其中使用它。
这是取消的实现:
class UsersRepository extends RepositoryBase<User> {
protected readonly COLUMNS = this.strictColumns({
firstName: "first_name",
lastName: "last_name",
email: "email"
});
} // okay
class BadUsersRepository extends RepositoryBase<User> {
protected readonly COLUMNS = this.strictColumns({
firstName: "first_name",
lastName: "last_name",
email: "email",
extra: "extra" // error! string is not assignable to never
});
}
如果需要,可以将其包装为类。
最后是用法:
doSomethingInSeries
请记住,任务不会立即取消,因此不会在5秒后准确调用继续(等待,捕获或捕获)。为了保证您可以将此方法与@Bergi方法结合使用。