【问题标题】:What is the proper pattern for bubbling and catching exceptions using async/await?使用 async/await 冒泡和捕获异常的正确模式是什么?
【发布时间】:2019-02-03 14:19:42
【问题描述】:

我正在努力弄清楚在嵌套等待/异步例程中处理错误的正确模式是什么,同时又要保持代码简洁明了。 (尽管阅读了无数的文章和博客)

我有一组(基本上)类似于以下的函数:

async validate(params) {
    const recCount = await this._getCount(db, params);

    if( recCount > 0 )
        return "Record already exists";
}

_getCount 是创建 sql 的包装器

async _getCount(conn, regdata) {
    const sql = "SELECT count(*) AS 'count' FROM myTable WHERE product = ? and category = ?";
    let rows = await this._execSQL(conn, sql, [ regdata.product, regdata.category ]);
    return rows[0].count;
}

实际查询执行如下:

async _execSQL(conn, sql, data) {
    const [ rows ] = await conn.query(sql, data);
    return rows;
}

如果查询失败,方法 conn.query(来自 mysql2/promise 库)将拒绝承诺。

所以,我的问题变成了处理异常的正确模式是什么?

在同步世界中,我对 _execSQL 和 _getCount 无能为力,只能在 validate 中捕获异常;只是自然地让异常冒泡。

但是,在异步世界中,我如何在不出现“未处理的承诺”异常的情况下进行等效操作?

我是否坚持必须在每个异步例程中一直捕获错误?

或者有没有更好的方法而不使用像 process.on('unhandledRejection',...) 这样的东西,感觉就像我在规避问题?

编辑:添加示例和堆栈跟踪

好的,所以我实际上已将此代码添加到我的应用程序中,并将 try/catch 放入 validate 函数中。逐字代码是:

async validate(db, params) {
    let recCount;

    try {
        recCount = await this._getCount(db, params);
    } catch (err) {
        console.log('Caught error', err);
    }

    if (recCount > 0) return 'Record already exists';
}

async _getCount(conn, regdata) {
    const sql = "SELECT count(*) AS 'count' FROM myTable WHERE product = ? and category = ?";
    let rows = await this._execSQL(conn, sql, [ regdata.product, regdata.category ]);
    return rows[0].count;
}

async _execSQL(conn, sql, data) {
    const [ rows ] = await conn.query(sql, data);
    return rows;
}

我有一个用于 unhandledRejection 的事件处理程序,它会报告事件以及内部异常以及堆栈跟踪。这是它转储出来的:

Stack Trace:

AppError: Unhandled promise rejection.   Plugin may not be properly handling error.
    at process.on (D:\Development\website\service\server.js:73:5)
    at emitTwo (events.js:126:13)
    at process.emit (events.js:214:7)
    at emitPendingUnhandledRejections (internal/process/promises.js:108:22)
    at process._tickCallback (internal/process/next_tick.js:189:7)

Inner Error:

{   "message": "connect ECONNREFUSED 127.0.0.1:3306",   "code": "ECONNREFUSED",   "errno": "ECONNREFUSED" }

Error: connect ECONNREFUSED 127.0.0.1:3306
    at PromisePool.query (D:\Development\website\webhooks\node_modules\mysql2\promise.js:323:22)
    at Registration._execSQL (D:\Development\website\webhooks\plugins\registration.js:108:31)
    at Registration._logRequest (D:\Development\website\webhooks\plugins\registration.js:179:14)
    at Registration.register (D:\Development\website\webhooks\plugins\registration.js:52:8)
    at Router.exec (D:\Development\website\service\router.js:119:20)
    at IncomingMessage.request.on (D:\Development\website\service\server.js:292:47)
    at emitNone (events.js:106:13)
    at IncomingMessage.emit (events.js:208:7)
    at endReadableNT (_stream_readable.js:1064:12)
    at _combinedTickCallback (internal/process/next_tick.js:138:11)

【问题讨论】:

  • 万一有人落入了让我在这里停留了一段时间的陷阱,我无意中没有在之前的操作中添加await (_logRequest) 并且没有这样做它跑进去了与_getCount 平行。两者最终都抛出了一个错误,但只有一个被捕获。放入此示例代码有助于定位问题。所以....经验教训....确保你await 所有你想以串行方式运行的异步函数。

标签: node.js async-await node-mysql2


【解决方案1】:

您总是可以让拒绝冒泡并选择最佳级别来捕捉它们:

async function f1() { return await f2(); }
async function f2() { return await f3(); }
async function f3() {
  return Promise.reject('no way!');
  // or
  throw 'no way!';
}

async function f_await() {
  try {
    console.log('never succeeds here', await f1());
  } catch (err) {
    console.log('I check for errors at the level I prefer!');
    throw 'I can even intercept and rethrow!';
  }
  return 'or i can keep going like eveything is fine';
}
function f_then() {
  f1().then(console.log.bind(null, 'never succeeds here'))
  .catch(function (err) {
    console.log('I check for errors at the level I prefer!');
    throw 'I can even intercept and rethrow!';
  }).then(function () {
    return 'or i can keep going like eveything is fine';
  });
}

如果您触发未处理的拒绝警告,那是因为...您没有在链中的任何点处理某些拒绝,而您总是需要:即使在同步代码中,如果引发了异常并且从未被捕获,电脑会告诉你有多不开心。

如果您认为处理代码中拒绝的 SQL 查询的最佳方法是在 validate 中,那就去吧:用 try/catch 块和“句柄”包围这个 await catch 中的错误您认为最好的方式...不确定我在这里看到了问题!

【讨论】:

  • 那么这里可能发生了其他事情。如果我没有在 await conn.query 周围专门放置一个 try/catch 块,即使我在 validate 函数中有一个 try/catch,我最终也会收到 unhandledRejection 错误。
  • 即使您使用catch 停止传播,错误的堆栈跟踪也会将您引导至validate?似乎很奇怪。我会投票给“这里发生的其他事情”!
  • 用执行示例和堆栈跟踪更新了原始帖子。
  • 确实,没有提到validate,但是webhooks/plugins/registration.js中有一个register方法调用_logRequest,然后又调用_execSQL。所有这一切都是由您的Router 触发的,它显然声明了一条不...处理拒绝的路线。 ;)
  • 哈哈!我自己也看到了。这绝对是(L)用户错误!当!有一次,你看到的是你想看到的,而不是那里的东西。废话!
猜你喜欢
  • 1970-01-01
  • 2013-01-28
  • 2011-06-04
  • 1970-01-01
  • 2015-09-23
  • 2020-06-02
  • 2014-08-26
相关资源
最近更新 更多