Узел генерирует UnhandledPromiseRejectionWarning для запросов Mongoose, используя Promise.all с оператором .catch () - PullRequest
0 голосов
/ 26 февраля 2019

Я новичок в Node / Mongoose и пытаюсь корректно обрабатывать ошибки в скрипте, чтобы добавить игроков в лигу.В приведенном ниже коде явно сгенерированные и не связанные с Promise ошибки правильно распознаются оператором .catch (), но отклоненные обещания - нет.

Например, при попытке передать недопустимый идентификатор пользователя выдает User not found.

Но если я протестирую отклонение Promise путем отключения базы данных, я получу следующее:

(node:6252) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): MongoNetworkError: failed to connect to server [localhost:27017] on first connect [MongoNetworkError: connect ECONNREFUSED 127.0.0.1:27017]

Неужели я как-то неправильно использую Promise.all () и .catch ()?

Просто чтобы прояснить, я пытаюсь понять, почему ошибка не обрабатывается, а не почему выдается ошибка.

Мой сценарий:

const 
mongoose = require('mongoose'),
User = require('./models/users'),
League = require('./models/leagues'),
dbUrl = process.env.DBURL || 'mongodb://localhost/predictor';

mongoose.connect(dbUrl, { useNewUrlParser: true });

const addUserToLeague = (userId, leagueId) => {
    let foundUser = User.findById(userId);
    let foundLeague = League.findById(leagueId);

    return Promise.all([foundUser, foundLeague])
    .then(arr => {
        if(!arr[0]){
            throw 'User not found';
        }else if(!arr[1]){
            throw 'League not found';
        }
        return arr;
    })
    .then(arr => {
        arr[0].leagueMemberships.push(arr[1]);
        arr[1].users.push(arr[0]);
        return arr;
    })
    .then(updatedArr => {
        updatedArr[0].save();
        updatedArr[1].save();
        return updatedArr;
    })
    .then(updatedArr => { console.log(`User ${updatedArr[0]._id} added to league ${updatedArr[1]._id}`) })
    .catch(err => { console.log('Error:', err) });
};

addUserToLeague(process.argv[2], process.argv[3]); // Needs 2 args: User ID and League ID

1 Ответ

0 голосов
/ 26 февраля 2019

Как указал Берги, ошибка будет выглядеть как connect, которая возвращает обещание , которое вы вообще не выполняете, в том числе не дожидаетесь его завершения.Таким образом, как минимум, вам нужно справиться с этим:

const connectionPromise = mongoose.connect(dbUrl, { useNewUrlParser: true })
    .catch(error => {
        // Handle connection error
    });

Затем в addUserToLeague:

const addUserToLeague = (userId, leagueId) => {
    return connectionPromise.then(connection => {
        // ...logic here
    });
};

... но , я спрашиваю васдолжно быть подключено, когда модуль загружен таким образом, а не передавать соединение в addUserToLeague.


Помимо этого, фактическое использование Promise.all в порядке, но:

  1. Можно надеяться, что findById не разрешит обещание с ложным значением, если элемент не найден, так что весь первый обработчик then кажется ненужным.
  2. Предположительно save возвращает обещание.Вы не обрабатываете отклонение или не ждете разрешения тех, кто так.
  3. Я бы использовал деструктуризацию, чтобы избежать arr[0] и arr[1], так как заказ легко забыть.
  4. Нет никакой причины, чтобы обработчик then с вызовами push был отделен от обработчика then, выполняющего сохранение.
  5. addUserToLeague должен возвращать результат цепочки обещаний, так что вызов кодаэто A) Знает, когда оно закончено, и B) Знает, когда оно терпит неудачу.
  6. Ошибки не должны обрабатываться в addUserToLeague;вместо этого обработайте их в вызывающей стороне.
  7. Существует также проблема, заключающаяся в том, что данные денормализованы : вы храните информацию о членстве как в пользовательском объекте, так и вобъект лиги.Может быть, это относительно нормально для баз данных документов (я бы не знал);в СУБД вы бы хранили информацию в одноместном месте.Причина ясна из кода в addUserToLeague: что делать, если сохранение пользователя происходит успешно, но сохранение лиги не удается?Затем пользовательский объект говорит, что он является членом лиги, а объект лиги не говорит, что он является членом.Также существует проблема, заключающаяся в том, что, поскольку она хранится в двух местах, даже если ничего не происходит неправильно, в течение короткого периода один из (пользователь или лига) будет сохранен, а другой - нет.Оба являются проблемами целостности.Если вы можете нормализовать хранение этой информации в одном месте, это было бы хорошо.Если вы не можете это сделать, вам нужно обновить код, чтобы он сохранил один из них, дождался его успешного выполнения, сохранил другой, а в случае неудачи попытается отменить изменение первого.

Примерно так (я не пытаюсь решить проблему нормализации здесь, это большая картина):

const 
mongoose = require('mongoose'),
User = require('./models/users'),
League = require('./models/leagues'),
dbUrl = process.env.DBURL || 'mongodb://localhost/predictor';

const addUserToLeague = (connection, userId, leagueId) => {
    return Promise.all([
        User.findById(userId),
        League.findById(leagueId)
    ])
    .then(([user, league]) => {
        user.leagueMemberships.push(league);
        league.users.push(user);
        return Promise.all([user.save(), league.save()]);
    })
    .then((([user, league]) => {
        console.log(`User ${user._id} added to league ${league._id}`);
    });
};

mongoose.connect(dbUrl, { useNewUrlParser: true })
.then(connection => addUserToLeague(connection, process.argv[2], process.argv[3]) // Needs 2 args: User ID and League ID
.catch(error => {
    // Handle/report error
});

Если вы используете какой-либо недавний вариант Node, вы можете использовать async функция:

const 
mongoose = require('mongoose'),
User = require('./models/users'),
League = require('./models/leagues'),
dbUrl = process.env.DBURL || 'mongodb://localhost/predictor';

const addUserToLeague = async (connection, userId, leagueId) => {
    let [user, league] = await Promise.all([
        User.findById(userId),
        League.findById(leagueId)
    ]);
    user.leagueMemberships.push(league);
    league.users.push(user);
    [user, league] = await Promise.all([user.save(), league.save()]);
    console.log(`User ${user._id} added to league ${league._id}`);
};

mongoose.connect(dbUrl, { useNewUrlParser: true })
.then(connection => addUserToLeague(connection, process.argv[2], process.argv[3]) // Needs 2 args: User ID and League ID
.catch(error => {
    // Handle/report error
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...