为什么会收到“ UnhandledPromiseRejectionWarning”?

时间:2019-06-13 22:25:56

标签: javascript node.js async-await

我正在尝试调用异步方法。异步方法是:

async connect() {
        this.pool = await mysql.createPool(this.conf);
        await this.pool.getConnection().catch((err) => { throw new Error(err) });
}

调用此方法的代码为:

(async () => {
    await db.connect()
        .catch((err) => {
            console.error(`Database connection error: ${err.message}`);
            throw new Error(err);
        });
})();

但是,当this.pool.getConnection()中发生错误时,我会收到以下警告:

[Thu Jun 13 2019 23:10:59] [ERROR]  (node:18296) UnhandledPromiseRejectionWarning: Error: Error: Access denied for user 'root'@'localhost' (using password: YES)
    at pool.getConnection.catch
    at process._tickCallback
[Thu Jun 13 2019 23:10:59] [ERROR]  (node:18296) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
[Thu Jun 13 2019 23:10:59] [ERROR]  (node:18296) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

在阅读了类似问题的答案后,我了解到必须处理Promises的拒绝,但是我迷失了我未正确处理此问题的地方。 this.pool.getConnection()调用具有catch块,并且在其中引发了错误,因此将其传递给匿名调用函数进行处理。

我已经尝试过对该代码进行多种变体,包括向匿名函数本身添加catch块,然后在该函数中再次引发错误,但似乎无济于事,并且警告始终源自this.pool.getConnection()调用

我要实现的目的是将connect()方法中发生的任何错误传递给匿名函数进行处理。

我要去哪里错了?预先感谢。

编辑:有趣的是,如果我按如下方式编写this.pool.getConnection()行(catch块中没有任何内容),则警告消失:

await this.pool.getConnection().catch((err) => {});

当我尝试在箭头功能内执行任何操作时,例如将错误分配给变量后,警告就会浮出水面。

2 个答案:

答案 0 :(得分:1)

在此代码中,您未处理错误:

(async () => {
    await db.connect()
        .catch((err) => {
            console.error(`Database connection error: ${err.message}`);
            throw new Error(err);
        });
})();

如果db.connect()拒绝,那么您的.catch()将被呼叫并重新抛出。这意味着await db.connect()将看到拒绝的诺言,并且包装器异步函数将返回您没有处理程序的拒绝的诺言。因此,您会遇到无法处理的拒绝。

您可以做到这一点,从而使错误完全在async函数中处理,从而永远不会返回被拒绝的承诺,或者您可以正确处理错误。

仅供参考,将await.catch()混合使用也很奇怪(通常不建议这样做)。通常,您将.then().catch()一起使用,而awaittry/catch一起使用。


如果您要进行的所有错误处理都是记录错误并避免出现警告,则可以更改为:

db.connect().catch((err) => {
    console.error(`Database connection error: ${err.message}`);
    // put any other required error handling here
});

通过删除throw err,可以在本地“处理”错误,因此不会出现未处理的拒绝。您在这里的唯一拒绝已得到处理。另外,请注意,似乎不需要asyncawait,因为您只是在进行函数调用并处理任何异常。


注意,设计模式:

async function foo() {
    await f();
}

与以下内容几乎相同:

function foo() {
    return f();
}

唯一不同的地方是f()不返回承诺或不同步抛出,这两种情况都不应在行为正常的承诺返回API中发生。

由于我更喜欢​​能够实现目标的最简单的代码表达式,因此我不建议使用async/await,除非它实际上可以帮助您简化代码。

答案 1 :(得分:0)

事实证明,尽管我的代码在任何方面都不理想,但问题的真正根源在于,除了匿名函数之外,我还从其他地方调用db.connect()。该另一个调用没有用于错误处理的catch块,因此出现UnhandledPromiseRejection警告。一旦删除了对db.connect()的重复调用,警告就消失了。

作为参考,以下是在贡献者的评论/答案的帮助下改进的工作代码:

数据库类中的方法:

async connect() {
    this.pool = await mysql.createPool(this.conf);
    return await this.pool.getConnection();
}

调用它:

db.connect()
  .then(result => {
      console.log(result)
  })
  .catch(err => {
      console.error(`Database connection error: ${err.message}`);
  });