Node.js UnhandledPromiseRejectionWarning 即使在捕获它之后

Posted

技术标签:

【中文标题】Node.js UnhandledPromiseRejectionWarning 即使在捕获它之后【英文标题】:Node.js UnhandledPromiseRejectionWarning even after catching it 【发布时间】:2017-05-05 22:42:49 【问题描述】:

我正在使用具有新 async/await 功能的 Node 7.2.1。我也在像这样使用带有猫鼬的 Native ES6 Promises -

const mongoose = require('mongoose');
mongoose.Promise = global.Promise;

我的代码流程是这样的——

async function getFollowers()
    try 
        const followers = await User.getFollowersFromMongo(req.params.userId);
        res.send(followers);
     catch (err) 
        winston.error('Printing Error = ', err);
        res.status(400).send(success: false, error: err);
    


UserSchema.statics.getFollowersFromMongo = async(userId) => 
    try 
        let aggregateQuery = []; //some syntactical error in mongo query to produce exception

        const followers = await User.aggregate(aggregateQuery);
        return followers.map(follower => follower.followerData);
    
    catch (err) 
        return Promise.reject(err);
    
;

这段代码工作得很好。当产生一些错误时就会出现问题。所以我特意修改了我的 mongoose 查询,以便 MongoDB 会抛出错误。

现在 MongoDB 正如预期的那样抛出一个错误,该错误被我的代码完全捕获并返回给客户端并返回 400 错误代码。

问题是即使错误(故意)已被我发现,Node.js 仍然给我这个警告 -

error:  Printing Error = MongoError: path option to $unwind stage should be prefixed with a '$': followerData
at Function.MongoError.create (/home/node_modules/mongodb-core/lib/error.js:31:11)
at /home/node_modules/mongodb-core/lib/connection/pool.js:483:72
at authenticateStragglers (/home/node_modules/mongodb-core/lib/connection/pool.js:429:16)
at Connection.messageHandler (/home/node_modules/mongodb-core/lib/connection/pool.js:463:5)
at Socket.<anonymous> (/home/node_modules/mongodb-core/lib/connection/connection.js:317:22)
at emitOne (events.js:96:13)
at Socket.emit (events.js:188:7)
at readableAddChunk (_stream_readable.js:176:18)
at Socket.Readable.push (_stream_readable.js:134:10)
at TCP.onread (net.js:551:20)

GET /user/385/followers 400 39.868 ms - 263

(node:10158) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): MongoError: path option to $unwind stage should be prefixed with a '$': followerData
(node:10158) 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.

可以看出我的请求返回了 400 状态,并且我的错误日志也从初始方法的 catch 块中打印出来,但是 Node.js 仍然说错误消息没有被处理.

为什么即使在相同的错误被捕获后还是这样说?

更新 - 感谢@dvlsg 和@Bergi,该错误已在版本 4.7.5 中得到修复

【问题讨论】:

我猜是因为你用 try/catch 而不是 .catch() 来捕捉它。 @KevinB 但这不是 async/await 处理错误的方式吗? 相关:***.com/questions/40500490/… 不使用.catch() 似乎不是问题。 This 是另一个使用相同方法的示例,它不会产生 unhandled 堆栈跟踪。 此示例没有未处理错误的原因是因为caller1catch 来自Promise.reject(err) 来自caller2 【参考方案1】:

mongoose 聚合如何使用 async/await 显然有些奇怪。对我来说,这似乎是一个错误。如果是的话,一定要报告给猫鼬。

谢天谢地,有一个简单的解决方法:

const followers = await User.aggregate(aggregateQuery).exec();

添加显式 .exec() 可以让我按预期捕获聚合管道错误。


我认为增加混乱的根本问题是有一个额外的Promise 漂浮在周围,被拒绝且未处理。因为从技术上讲,您 正确地处理了这里的预期拒绝。否则你不会看到 Printing error = ... 被记录。

这就是我认为正在发生的事情--

await User.aggregate() Aggregate#then() 是通过 awaitthenables 一起调用的(我认为) Aggregate#exec() 在内部由 Aggregate#then() 调用 注意a callback is provided 到exec() Aggregate#exec() 内部的新Promise 是created,和will be rejected 这个是未处理的Promise,我相信。 由于从Aggregate#then()Aggregate#exec() 提供了回调,所以Aggregate#exec() 中的Error 将是provided to the callback 在Aggregate#then() 的回调中,新的created Promise 被拒绝 我相信这个Promise 会按预期处理,因为它是来自Aggregate#then() 的返回

我想我可以通过注释掉猫鼬Aggregate 定义中的this line 来证实我的怀疑。这将防止未处理的拒绝处理程序被击中。顺便说一句,我并不是建议这样做。这只是额外的证据,而不是解决方案,因为现在我只有一个未被拒绝的Promise


这是一种在自包含代码中重现未被捕获的拒绝的最小方法,使用 node --harmony-async-await 运行(在节点 v7.2.1 上测试)

const mongoose = require('mongoose');
mongoose.Promise = global.Promise;
mongoose.connect('mongodb://localhost/temp');

const userSchema = new mongoose.Schema(
  name: 'string'
);

const User = mongoose.model('User', userSchema);

async function run() 
  try 
    await User.aggregate([]);
  
  catch (err) 
    console.log('caught expected error:', err);
  


run();

【讨论】:

如果是这种情况,绝对应该将它作为一个错误报告给mongoose。 感谢源代码的链接,正如我所怀疑的那样。解决方法应该是将then 方法更改为简单的function(onfulfill, onreject) return this.exec().then(onfulfill, onreject); 、avoiding the Promise constructor antipattern,这会通过exec 回调不必要地创建第二个promise。您要打开 GH 问题吗? 当然可以。 问题可用here。 是的,在查询末尾应用 .exec() 不会产生错误。我会关注 github 问题,如果你的推理被证明是正确的,那么我很乐意接受这个答案。

以上是关于Node.js UnhandledPromiseRejectionWarning 即使在捕获它之后的主要内容,如果未能解决你的问题,请参考以下文章

[Node.js]如何在IDEA中配置Node.js

node.js 初识node.js,运行在服务端的 JavaScript

node.js教程基础:node.js命令行选项

Node.js HTTP模块

Node.js基础:第一篇

270 Node.js快速入门:Node.js 的组成,Node.js基础语法,Node.js全局对象global