Асинхронный запрос нескольких коллекций MongoDB в Node.js

Мне нужно получить две разные коллекции MongoDB (db.stats и db.tables) для одного и того же запроса req.

Теперь в приведенном ниже коде я вкладываю запросы в функцию обратного вызова.

router.post('/', (req, res) => {
let season = String(req.body.year);
let resultData, resultTable;
db.stats.findOne({Year: season}, function (err, data) {
    if (data) {
        resultData = getResult(data);
        db.tables.findOne({Year: season}, function (err, data) {
            if (data) {
                resultTable = getTable(data);
                res.render('index.html', {
                    data:{
                        result : resultData,
                        message: "Working"}
                });
            } else {
                console.info("Error in Tables");
            }
        });
    } else {
        console.info("Error in Stats");
        }
    });
});

Этот код работает, но есть некоторые вещи, которые кажутся неправильными. Итак, мой вопрос:

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
759
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

То, что у вас есть прямо сейчас, в JavaScript называется обратный вызов ад. Здесь пригодится Promises.

Вот что вы можете сделать:

router.post('/', (req, res) => {
  let season = String(req.body.year);
  var queries = [
    db.stats.findOne({ Year: season }),
    db.tables.findOne({ Year: season })
  ];

  Promise.all(queries)
  .then(results => {
    if (!results[0]) {
      console.info("Error in Stats");
      return; // bad response. a better way is to return status 500 here
    } else if (!results[1]) {
      console.info("Error in Tables");
      return; // bad response. a better way is to return status 500 here
    }
    let resultData = getResult(results[0]);
    let resultTable = getTable(results[1]);

    res.render('index.html', { data: {
      result : resultData,
      message: "Working"
    } });
  })
  .catch(err => {
    console.info("Error in getting queries", err);
    // bad response. a better way is to return status 500 here
  });
});

Похоже, вы используете Mongoose в качестве ODM для доступа к базе данных mongo. Если вы не передаете функцию в качестве второго параметра, значение, возвращаемое вызовом функции (например, db.stats.findOne({ Year: season })), будет Promise. Мы поместим все эти неразрешенные обещания в массив и вызовем Promise.all для их разрешения. Используя Promise.all, вы ждете, пока все ваши запросы к базе данных будут выполнены, прежде чем переходить к визуализации вашего представления index.html. В этом случае результаты вызовов функций вашей базы данных будут храниться в массиве results в порядке вашего массива queries.

Кроме того, я бы рекомендовал делать что-то вроде res.status(500).send("A descriptive error message here") всякий раз, когда возникает ошибка на стороне сервера в дополнение к вызовам console.info.

Вышеупомянутое решит вашу проблему с вложенной структурой, но последняя проблема все равно останется (т.е. клиентская сторона не отвечает при обработке этих запросов). Чтобы решить эту проблему, вам необходимо сначала определить свое узкое место. Вызов каких функций занимает больше всего времени? Поскольку вы используете findOne, я не думаю, что это будет узким местом, если только соединение между вашим сервером и базой данных не имеет проблем с задержкой.

Я собираюсь предположить, что запрос POST не выполняется через AJAX, поскольку в нем есть res.render, поэтому эта проблема не должна быть вызвана каким-либо клиентским кодом. Я подозреваю, что один из getResult или getTable (или оба) занимает довольно много времени, учитывая тот факт, что это приводит к тому, что клиентская сторона не отвечает. Каков размер данных, когда вы запрашиваете свою базу данных? Если размер запроса настолько велик, что на его обработку уходит много времени, я бы порекомендовал изменить способ выполнения запроса. Вы можете использовать AJAX во внешнем интерфейсе, чтобы отправить запрос POST в серверную часть, который затем вернет ответ в виде объекта JSON. Таким образом, страницу в браузере не нужно будет перезагружать, и вы получите лучший пользовательский интерфейс.

мне нравится ваш ответ больше, чем мой собственный настолько подробный (:. Я предлагаю вам начать работу с async, подождите, это намного проще

Amit Wagner 10.03.2018 20:31

Драйвер mongodb возвращает обещание, если вы не отправляете обратный вызов, поэтому вы можете использовать async await

router.post('/', async(req, res) => {
    let season = String(req.body.year);
    let resultData, resultTable;
    try {
        const [data1,data2] = await Promise.all([
            db.stats.findOne({Year: season}),
            db.tables.findOne({Year: season})
        ]);
        if (data1 && data2) {
            resultData = getResult(data1);
            resultTable = getTable(data2);
           return res.render('index.html', {
                data: {
                    result: resultData,
                    message: "Working"
                }
            });
        }
        res.send('error');
        console.info("Error");
    } catch (err) {
        res.send('error');
        console.info("Error");
    }

});

Другие вопросы по теме