【问题标题】:Javascript promises: UnhandledPromiseRejectionWarningJavascript 承诺:UnhandledPromiseRejectionWarning
【发布时间】:2020-03-01 22:55:28
【问题描述】:

我是 Javascript Promises 的新手。我正在尝试以基于 Promises 的样式执行 MySQL 查询,我已经摸不着头脑了半个多小时,但我仍然无法在这段代码中找到问题。我使用this article 作为参考。

mysql 文件

const mysql = require('mysql')
const Config = require('./Config.js');

let connection = mysql.createPool({
    connectionLimit: Config.mysql.connectionLimit,
    host:   Config.mysql.host,
    user: Config.mysql.user,
    password: Config.mysql.password,
    database: Config.mysql.database,
    multipleStatements: true,
});

module.exports = {
    query: function(query, binds) {
        return new Promise((resolve, reject)=>{
            connection.query(query, binds, (error, ret)=>{
                if(error)
                    return reject(error);
                else
                    resolve(ret);
            })
        })
    },

    close: function() {
        return new Promise((resolve, reject)=>{
            connection.end((error)=>{
                if(error)
                    return reject(error);
                else
                    resolve();
            });
        });
    }
}

实际脚本

const Mysql = require('mysql file');

try {

Mysql.query("SELECT `password` \
            FROM `m_users` \
            WHERE `email` = ?", 
            ['user@domain.tld'])
    .then(ret=>{
        if(ret.email)
            return res.send({
                params: req.body,
                message: 'Email address has already been taken.',
            })
    })
    .catch(error=>{
        throw error;

    })

catch( e )
{
    console.log(e); // is never reached
}

throw error 永远不会被解雇。相反,在我的节点控制台中,我收到以下错误:

(节点:8684)UnhandledPromiseRejectionWarning:错误:ER_NO_SUCH_TABLE:表“forge.m_users”不存在

........在这里回溯.......

(node:8684) UnhandledPromiseRejectionWarning:未处理的承诺拒绝。此错误源于在没有 catch 块的情况下抛出异步函数内部,或拒绝未使用 .catch() 处理的承诺。 (拒绝编号:1)

我知道该表不存在。我有兴趣找出为什么.catch 不处理错误。我究竟做错了什么? :/

【问题讨论】:

  • throw error is never fired 你确定吗?该语句创建一个未处理的拒绝。仔细检查该行是否正在运行 - 在其上方放置 console.log 或其他内容
  • 你是对的 - 它被解雇了。我迷路了;我应该如何处理拒绝?我已经有一个包含整个过程的 try/catch 块(请参阅更新的答案)
  • @user8555937 - 在非async 函数中,try/catch 无法捕获 Promise 拒绝,因为当 Promise 被拒绝时,执行已经离开了 @987654331 @块。

标签: javascript node.js promise


【解决方案1】:

这是一种反模式(但您到目前为止不是第一个这样做的人!):

.catch(error=>{
    throw error;

})

上面写着“创建一个新的承诺”(因为catch 创建了一个承诺)“如果原始承诺拒绝,则以完全相同的拒绝原因拒绝它(error)。”所以它基本上是无操作的,它什么都不做。它的承诺相当于:

try {
    // something
} catch (error) {
    throw error;
}

由于它不执行任何操作,请考虑一下没有它的代码会是什么样子 - 代码违反了 Promise 的基本规则之一,即“处理拒绝或将链传递给其他可以执行的操作”。 :-)

如果你想处理拒绝,不要throw from catch

Mysql.query("SELECT `password` \
        FROM `m_users` \
        WHERE `email` = ?", 
        ['user@domain.tld'])
.then(ret=>{
    if(ret.email) {
        return res.send({
            params: req.body,
            message: 'Email address has already been taken.',
        });
    }
})
.catch(error=>{
    // Use `error` here without `throw`ing -- report it, put it in a log, etc.
});

在对问题的更新中,您表明您的所有代码都使用了try/catch 中的承诺:

try {
    Mysql.query(/*...*/)
    .then(/*...*/)
    .catch(error=>{
        throw error;
    });
}
catch( e )
{
    console.log(e); // is never reached
}

在非async 函数中,try/catch 无法捕获 Promise 拒绝,因为当 Promise 被拒绝时,执行已经离开了 try 块。直接使用 Promise 时,您使用 catch 方法。或者您使用async 函数(更多内容见下文)。


旁注:Node.js 已经支持 async 函数有一段时间了,这往往更容易编写。在async 函数中,您的代码可能是:

// Within an `async` function
try {
    const ret = await Mysql.query("SELECT `password` \
            FROM `m_users` \
            WHERE `email` = ?", 
            ['user@domain.tld']);
    if(ret.email) {
        return res.send({
            params: req.body,
            message: 'Email address has already been taken.',
        });
    }
} catch (error) {
    // Use `error` here without `throw`ing -- report it, put it in a log, etc.
}

Using async at the top level of a module 也即将推出...


在您提出的评论中:

我正在构建一个 MVC API,并在同一个控制器方法中运行更多基于 Promise 的过程。一旦遇到异常,我想返回一条消息并停止继续执行下一个其他基于承诺的过程。我一定要使用 async/await 方法还是有其他方法?

如果您不想使用,则不必使用async/await,不;他们只是让事情变得更简单(在我看来)。

如果你不想使用async/await,在你传递then的实现回调中,返回下一个操作的承诺。这使得 Promise then 根据下一个操作的 Promise 执行或拒绝 - 在 Promise 行话中,它解析then 的 Promise从下一个操作开始。

代码值 1024 字,所以:

doSomethingAsync()
.then(value => {
    // ...use `value`, then
    return doSomethingElseAsync();
})
.then(() => {
    // ...
    return doYetAnotherAsyncThing();
})
.catch(error => {
    // Handle/report error here
});

如果任何一个 Promise 被拒绝,任何后续的履行处理程序都会被跳过,并且最后的拒绝处理程序(通过 catch 添加)被触发。

这大致是这个同步代码的承诺版本:

try {
    const value = doSomethingSync();
    // ...use `value`, then
    doSomethingElseSync();
    // ...
    doYetAnotherSyncThing();
} catch (error) {
    // Handle/report error here
}

【讨论】:

  • FWIW,我的新书将在几个月后出版。我将在第 8 章(以及第 9 章中的 async 函数)中详细介绍 Promise。有关详细信息,请参阅我的个人资料。
  • 感谢您的回复。我来自 PHP 背景。我正在构建一个 MVC API,并在同一个控制器方法中运行更多基于 Promise 的过程。一旦遇到异常,我想返回一条消息并停止继续执行下一个其他基于承诺的过程。我一定要使用async/await 方法还是有其他方法?
  • @user8555937 - 我已经添加到答案的末尾来回答这个问题。 :-) 编码愉快!
  • 感谢大家的回复,非常感谢! :)
【解决方案2】:

基本上,您收到未处理拒绝的原因是混合了异步和同步代码 - 承诺是异步的,try{ ... } catch() { ... } 仅在同步模式下工作。在同步 try-catch 块中调用异步函数不会等待它解决。尝试用async/await 包装你的promise 或处理promise 本身在.catch() 中的拒绝。

【讨论】:

    猜你喜欢
    • 2018-12-09
    • 1970-01-01
    • 1970-01-01
    • 2019-03-19
    • 2018-03-16
    • 2021-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多