Node.js UnhandledPromiseRejectionWarning即使在捕获之后也是如此

Jyo*_*ngh 6 javascript mongoose node.js async-await es6-promise

我正在使用Node 7.2.1和新的async/await功能.我也使用像这样的猫鼬本机ES6 Promises -

const mongoose = require('mongoose');
mongoose.Promise = global.Promise;
Run Code Online (Sandbox Code Playgroud)

我的代码流是这样的 -

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);
    }
};
Run Code Online (Sandbox Code Playgroud)

这段代码非常好用.出现错误时会出现问题.所以我故意修改我的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.
Run Code Online (Sandbox Code Playgroud)

可以看出我的请求已返回400状态,我的错误日志也已从初始方法的catch块打印,但Node.js仍然说不处理错误消息.

即使在捕获到相同的错误之后,为什么会这样说呢?

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

dvl*_*lsg 2

猫鼬聚合与 async/await 的配合方式肯定有些奇怪。对我来说,这似乎是一个错误。如果是的话,绝对应该报告给 mongoose。

值得庆幸的是,有一个简单的解决方法:

const followers = await User.aggregate(aggregateQuery).exec();
Run Code Online (Sandbox Code Playgroud)

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


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

这就是我相信正在发生的事情——

  • await User.aggregate()
  • Aggregate#then()通过与(我认为)await合作来调用thenables
  • Aggregate#exec()被内部调用Aggregate#then()
  • 一个新的Promise内部Aggregate#exec()创建,并且将被拒绝
    • 我相信,这是未处理的Promise
  • 由于回调被提供给Aggregate#exec()from Aggregate#then()Error内部Aggregate#exec()将被提供给回调
  • 在回调中Aggregate#then(),新创建的 Promise被拒绝
    • 我相信这Promise是按预期处理的,因为它是从Aggregate#then()

我想我可以通过在猫鼬定义中注释掉这一行Aggregate来证实我的怀疑。这将防止未处理的拒绝处理程序被击中。顺便说一句,我并不是建议这样做。这只是额外的证据,而不是解决方案,因为现在我只有一个未被拒绝的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();
Run Code Online (Sandbox Code Playgroud)

  • 如果是这种情况,绝对应该将其作为错误报告给 mongoose。 (2认同)
  • 可用问题[此处](https://github.com/Automattic/mongoose/issues/4824)。 (2认同)