try/catch 块未捕获异步/等待错误

ken*_*ken 2 javascript error-handling async.js

我有一个简单的timeout函数,它包装了一个带有超时的异步函数,以确保它在预设的时间后失败。超时函数如下:

export default async function<T = any>(
  fn: Promise<T>,
  ms: number,
  identifier: string = ""
): Promise<T> {
  let completed = false;
  const timer = setTimeout(() => {
    if (completed === false) {
      const e = new Error(`Timed out after ${ms}ms [ ${identifier} ]`);
      e.name = "TimeoutError";
      throw e;
    }
  }, ms);
  const results = await fn;
  completed = true;
timer.unref();

  return results;
}
Run Code Online (Sandbox Code Playgroud)

然后我在这个简单的代码片段中使用这个函数来确保获取请求(使用node-fetch实现)被转换为文本输出:

let htmlContent: string;
  try {
    htmlContent = await timeout<string>(
      response.text(),
      3000,
      `waiting for text conversion of network payload`
    );
  } catch (e) {
    console.log(
      chalk.grey(`- Network response couldn\'t be converted to text: ${e.message}`)
    );
    problemsCaching.push(business);
    return business;
  }
Run Code Online (Sandbox Code Playgroud)

在多次迭代中运行此代码时,大多数 URL 端点提供了一个可以轻松转换为文本的有效负载,但偶尔会出现一个似乎只是挂起fetch调用的负载。在这些情况下,超时实际上会触发,但TimeoutError抛出的超时并未被 catch 块捕获,而是终止了正在运行的程序。

我有点困惑。我现在确实经常使用 async/await,但我的理解可能仍然有一些粗糙的地方。谁能解释我如何有效地捕获此错误并处理它?

Cer*_*nce 5

抛出的错误只有在其直接封闭的函数具有某种错误处理时才会被捕获。您传递给的匿名函数setTimeout不是async函数本身,因此async如果在一段时间后单独 timeout抛出,该函数不会停止执行:

const makeProm = () => new Promise(res => setTimeout(res, 200));
(async () => {
  setTimeout(() => {
    throw new Error();
  }, 200);
  await makeProm();
  console.log('done');
})()
  .catch((e) => {
    console.log('caught');
  });
Run Code Online (Sandbox Code Playgroud)

这看起来是使用的好时机Promise.race:将 传递给它fetch Promise,并Promise在传递的ms参数之后传递一个拒绝的a :

async function timeout(prom, ms) {
  return Promise.race([
    prom,
    new Promise((res, rej) => setTimeout(() => rej('timeout!'), ms))
  ])
}

(async () => {
  try {
    await timeout(
      new Promise(res => setTimeout(res, 2000)),
      500
    )
   } catch(e) {
      console.log('err ' + e);
   }
})();
Run Code Online (Sandbox Code Playgroud)