异步/等待超时

时间:2016-05-09 15:55:15

标签: node.js typescript timeout promise async-await

我使用的是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!!
});

有关如何解决的任何想法?

3 个答案:

答案 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方法结合使用。