Узел.JS UnhandledPromiseRejectionWarning даже после его ловли

Я использую узел 7.2.1 с новым async / await характеристика. Я также использую родные обещания ES6 с мангустой, как это -

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);
    }
};

этот код работает абсолютно нормально. Проблема возникает, когда возникает какая-то ошибка. Поэтому я намеренно изменил свой мангуста запрос так что MongoDB выдаст ошибку.

Теперь MongoDB, как и ожидалось, выдает ошибку, которая отлично пойманный моим кодом и возвращенный клиенту с кодом ошибки 400.

проблема в том, что, хотя ошибка(преднамеренная) была поймана мной,узел.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 исходного метода, но узел.js о том, что сообщение об ошибке не обрабатываются.

почему говорит ли он это даже после того, как та же ошибка была поймана?

обновление - благодаря @dvlsg и @Bergi, ошибка была исправлена в версии 4.7.5

1 ответов


определенно, кажется, что-то странное с тем, как Mongoose aggregate играет с async/await. Мне кажется, это жук. Если это так, то об этом обязательно следует сообщить Мангусту.

к счастью, есть легко обойти:

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

добавление явного .exec() позволяет мне поймать ошибку aggregate pipeline, как ожидалось.


я думаю, что основная проблема, добавляющая к путанице здесь, заключается в том, что есть дополнительный Promise плавающий вокруг, который отклоняется и не обрабатывается. Потому что технически, ты are обработка ожидаемого отклонения здесь правильно. Иначе вы бы этого не увидели!--4--> журнал.

вот что я думаю, происходит...

  • вы await User.aggregate()
  • Aggregate#then() вызывается через await работает с thenables (Я думаю)
  • Aggregate#exec() вызывается внутри Aggregate#then()
  • новая Promise внутри Aggregate#exec() is создано и будут отклонены
    • этой - это необработанное Promise, Я считаю.
  • так как обратный вызов предоставляется Aggregate#exec() С Aggregate#then() на Error внутри Aggregate#exec() будет при условии обратного вызова
  • внутри функции обратного вызова в Aggregate#then(), недавно создано Promise отклонено
    • я верю в это Promise обрабатывается как ожидалось, так как это возврат от Aggregate#then()

я думаю, что могу подтвердить свои подозрения, комментируя эта строка в мангуста Aggregate определение. Это предотвратит попадание обработчика необработанного отклонения. Между прочим, я не собираюсь этого делать. Это просто дополнительные доказательства, а не решение, так как теперь у меня просто есть unrejected 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();