【问题标题】:NodeJS - Properly catch errors of async functionsNodeJS - 正确捕获异步函数的错误
【发布时间】:2021-10-20 08:38:44
【问题描述】:

我正在编写一个脚本来通过 API 从 Google Cloud 指标中提取数据,当时我意外地发现我不知道如何正确捕获异步函数的错误。 :O

这是来自谷歌云的示例代码:

// Imports the Google Cloud client library
const monitoring = require('@google-cloud/monitoring');

// Creates a client
const client = new monitoring.MetricServiceClient();

/**
 * TODO(developer): Uncomment and edit the following lines of code.
 */
const projectId = 'XXXXXXXXX';

async function getMetrics() {

  const request = {
    name: client.projectPath(projectId),
    filter: 'metric.type="cloudsql.googleapis.com/database/cpu/utilization"',
    interval: {
      startTime: {
        // Limit results to the last 20 minutes
        seconds: Date.now() / 1000 - 60 * 1,
      },
      endTime: {
        seconds: Date.now() / 1000,
      },
    },
    // Don't return time series data, instead just return information about
    // the metrics that match the filter
    view: 'HEADERS',
  };

  // Writes time series data
  console.log('start')
  const [timeSeries] = await client.listTimeSeries(request);
  console.log('Found data points for the following instances:');
  timeSeries.forEach(data => {
    console.log(data.metric.labels.instance_name);
  });
}


getMetrics();

函数listTimeSeries 返回一个承诺。我收到一个错误,我需要通过身份验证才能执行该操作,没有问题。

问题是我无法捕捉到该错误。
我尝试用 try {...} catch (err) {...} 块包围通话,没有被抓住。
我试图像这样const [timeSeries] = await client.listTimeSeries(request).catch(console.log); 抓住它 - 运气不好。

我一定是遗漏了一些东西,因为我对 nodeJS 还很陌生,并且不支持从异步函数中捕获错误。

我正在使用 nodeJS v14。

我错过了什么?
提前谢谢!


编辑

根据要求(@CherryDT),这是完整的错误输出:

我希望它不会太模糊。


编辑

事实证明,我一直在尝试捕获错误的方式很好。
问题的发生是因为listTimeSeries 函数(来自外部库)引发了错误,而不是拒绝了无法捕获的承诺。

谢谢各位。????

【问题讨论】:

  • 他们俩都应该这样做。你能显示完整的错误信息和堆栈吗?也许它是异步抛出的(这意味着库中的一个错误)
  • 听起来client.listTimeSeries() 的代码内部可能存在问题,并且它没有正确地将错误传播回调用者。
  • 同意其他人认为它被奇怪地抛出,您是否有权访问 metric_service_client.js:220?可以提供一些见解
  • 啊,是的,它可能在异步函数中抛出:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… 在这种情况下转到“抛出错误时遇到问题”,我认为没有办法捕捉它

标签: node.js asynchronous error-handling async-await try-catch


【解决方案1】:

请注意,我指的是“异步函数”和“异步函数”。在 Javascript 中,“异步函数”是指 function created with the async keyword,而当我说“异步函数”时,我指的是传统意义上的任何异步运行的函数。在 Javascript 中,使用 async 关键字创建的函数实际上只是底层的 Promise。

如果可以捕获异步函数(在 Promise 中)抛出的错误,您的代码工作。不幸的是,they can't。除非函数使用async functionsyntax,否则promise 中的错误必须用reject 包装。请参阅MDN example 了解我们在这里看到的问题:

// Throwing an error will call the catch method most of the time
var p1 = new Promise(function(resolve, reject) {
  throw new Error('Uh-oh!');
});

p1.catch(function(e) {
  console.error(e); // "Uh-oh!"
});

// Errors thrown inside asynchronous functions will act like uncaught errors
var p2 = new Promise(function(resolve, reject) {
  setTimeout(function() {
    throw new Error('Uncaught Exception!');
  }, 1000);
});

p2.catch(function(e) {
  console.error(e); // This is never called
});

// Errors thrown after resolve is called will be silenced
var p3 = new Promise(function(resolve, reject) {
  resolve();
  throw new Error('Silenced Exception!');
});

p3.catch(function(e) {
   console.error(e); // This is never called
});

我相信this is the code 在引发错误的库中,如下所示。请注意,另一个错误是正确的rejected。所有的cmets都是我的。

for (const methodName of metricServiceStubMethods) {
  const callPromise = this.metricServiceStub.then(
    stub => (...args: Array<{}>) => {
      if (this._terminated) {
        // This is the right thing to do!
        return Promise.reject('The client has already been closed.');
      }
      const func = stub[methodName];
      return func.apply(stub, args);
    },
    (err: Error | null | undefined) => () => {
      // If this was an async function (as in, using the keyword async,
      // not just literally an asynchronous function), this would work,
      // because the async keyword is just syntactic sugar for creating
      // a promise. But it's not so it can't be caught!
      throw err;
    }
  );

我相信,在这种情况下,很遗憾您无法捕捉到这个错误。

【讨论】:

  • 让我看看我是否明白这一点。我无法捕获错误的原因是因为它被抛出而不是拒绝承诺?
  • 从 Promise 中抛出,是的。如果我确实找到了您正在使用的同一个库,我相信就是这种情况。
  • 顺便说一句,我认为这是谷歌代码的一个错误,我不确定他们为什么这样写。
【解决方案2】:

你可以这样做。

(async function() {
    try {
      await getMetrics();
    } catch(error) {
      console.log("Error occured:", error);
    }
})();

请注意,如果您试图捕捉Promise 中的错误,您可以使用.then(() =&gt; { }).catch(err =&gt; { }) 样式,但对于async/await,您将需要try { } catch(err) { } 样式来捕捉错误。

编辑

通过这样做,它必须在 Promise 被拒绝时捕获任何错误。如果您仍然无法捕捉到错误,这意味着您使用的库没有正确地reject 承诺 (Promise.reject()),而是在承诺中硬编码 throw error 而不是拒绝一个。对于这种情况,您无法对错误捕获做任何事情。

【讨论】:

  • 我认为你的 -1 是因为我尝试了这两种方法并在问题中提到了它
  • @HikeNalbandyan 通过这样做,它必须在承诺被拒绝时捕获任何错误。如果您仍然无法捕捉到错误,则意味着您使用的库没有正确地reject 承诺 (Promise.reject()),而是在承诺中硬编码 throw error 而不是拒绝承诺。对于这种情况,您无法对错误捕获做任何事情,但要在 Github 上报告问题。要解决您的问题,您需要设置服务帐户以使用 Google Cloud 对您的代码进行身份验证,以使错误消失。如果您清楚这一点,您可以删除您的 -1
  • 非常感谢您的解释。抱歉 Pakpoom,-1 不是我写的。
猜你喜欢
  • 1970-01-01
  • 2020-10-01
  • 2018-12-11
  • 2019-11-26
  • 2014-12-01
  • 1970-01-01
  • 2017-10-23
  • 1970-01-01
  • 2021-03-08
相关资源
最近更新 更多