Маршрутизация NodeJS —

Краткие сведения:

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

ОТМЕЧАТЬ:

Причина, по которой я использовал eventEmitter, заключается в том, что если я вложу два запроса db с Мангустом, он будет делать parent, то его childs, поэтому я должен был убедиться, что первый сделан для запуска второго. но это не проблема, код работает, но только в первый раз, когда я его запускаю. Никогда не было этой проблемы раньше, мне действительно интересно, что происходит

Код:

// loads people user has chatted with
router.get('/loadUserChats', function(req, res) {
    var hostID = req.query.id;
    let list = [];
    let readyToSend = true;

    // gets photos for each user in the 'list'
    eventEmitter.on('addPictureToObjects', function() {
        list.forEach(function(listItem, i) {
            User.findOne({_id: listItem.id}, 'photo', function(err, guest) {
                listItem.photo = guest.photo;
                // if this is the last one send json object back
                if (i+1 == list.length) {
                    res.json({list: list});
                }
            });
        });
    });

    // get the user
    User.findOne({_id: hostID}, function(err, user) {
        // checks if user has chatted at all
        if (user.chat) {
            // loops through the chat
            user.chat.forEach(function(chat) {
                var photoURL = "";
                console.log(chat.user);
                // push the chat info so later we can load messages
                // by its ID
                list.push({
                    id: chat.user.id,
                    name: chat.user.name
                });
            });
            // okay, now that we are done looping lets add photos
            // to each one of those so it looks better
            eventEmitter.emit('addPictureToObjects');
        }
    });
});

ОШИБКА:

events.js:160
  throw er; // Unhandled 'error' event
  ^

Error: Can't set headers after they are sent.
  at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:344:11)
  at ServerResponse.header (/home/knox97/Documents/agro/node_modules/express/lib/response.js:719:10)
  at ServerResponse.json (/home/knox97/Documents/agro/node_modules/express/lib/response.js:247:10)
  at /home/knox97/Documents/agro/routes/api.js:249:10
  at Query.<anonymous> (/home/knox97/Documents/agro/node_modules/mongoose/lib/model.js:3357:16)
  at /home/knox97/Documents/agro/node_modules/kareem/index.js:259:21
  at /home/knox97/Documents/agro/node_modules/kareem/index.js:127:16
  at _combinedTickCallback (internal/process/next_tick.js:67:7)
  at process._tickCallback (internal/process/next_tick.js:98:9)

Я на самом деле исправил проблему, переписав код и добавив фотографию в список объектов чата initialy, поэтому мне не нужно добавлять фотографии вручную, его более быстрое и умное решение, но мне действительно интересно, почему это не сработало, нравится или поделиться этим вопросом, чтобы мы оба могли узнать, ура!

1 ответ

  1. Извлеките излучатель событий из обработчика маршрута. При каждом запуске обработчика маршрута добавляется другой обработчик 'addPictureToObjects'событий . Вы действительно хотите выдать это событие по каждому запросу, но вы не хотите регистрировать обработчик событий по каждому запросу, только один раз.

    Вы получаете ошибку"Can't set headers after they are sent.", потому что первый обработчик событий запускается, повторяет все элементы list, а затем вызываетres.json, который отправляет ответ обратно клиенту. Но так как вы зарегистрировали несколько обработчиков событий с каждым запросом, он выполняет этот код дважды (или три раза, или четыре раза и т. д. в зависимости от того, сколько раз вы попадали на этот маршрут и регистрировали новый обработчик событий каждый раз). При последующих запусках этого кода, когда он попадает res.jsonво второй раз, он ошибается, говоря, что больше не может изменить ответ, потому что он уже был отправлен обратно клиенту.

    // gets photos for each user in the 'list'
    eventEmitter.on('addPictureToObjects', function(list, res) {
      list.forEach(function(listItem, i) {
        User.findOne({_id: listItem.id}, 'photo', function(err, guest) { 
          listItem.photo = guest.photo;
          // if this is the last one send json object back
          if (i+1 == list.length) {
            res.json({list: list});
          }
        });
      });
    });    
    
    // loads people user has chatted with
    router.get('/loadUserChats', function(req, res) {
      var hostID = req.query.id;
      let list = [];
      let readyToSend = true;
    
      // get the user
      User.findOne({_id: hostID}, function(err, user) {
        // checks if user has chatted at all
        if (user.chat) {
          // loops through the chat
          user.chat.forEach(function(chat) {
            var photoURL = "";
            console.log(chat.user);
            // push the chat info so later we can load messages
            // by its ID
            list.push({
              id: chat.user.id,
              name: chat.user.name
            });
          });
          // okay, now that we are done looping lets add photos
          // to each one of those so it looks better
          eventEmitter.emit('addPictureToObjects', list, res);
        }
      });
    });
    

    Вы также можете просто изменить, eventEmitter.oneventEmitter.onceи это исправит вашу проблему. .onceрегистрирует обработчик событий, но затем отменяет его регистрацию после выполнения. Тем не менее, это кажется бессмысленным в коде, поскольку не кажется, что есть причина регистрировать обработчик и отменять его регистрацию снова и снова с каждым запросом, поэтому регистрация обработчика событий вне обработчика запроса кажется идеальным решением.


    Это не был ваш фактический вопрос, но вам не нужен эмиттер события здесь. Он все еще выполняет первый запрос db, затем в обратном вызове для этого запроса он испускает событие, чтобы сделать второй. Он все еще выполняет одну просьбу, а затем другую. Излучатель событий этого не изменил. Следующий код эквивалентен приведенному выше:

    // loads people user has chatted with
    router.get('/loadUserChats', function(req, res) {
      var hostID = req.query.id;
      let list = [];
      let readyToSend = true;
    
      // get the user
      User.findOne({_id: hostID}, function(err, user) {
        // checks if user has chatted at all
        if (user.chat) {
          // loops through the chat
          user.chat.forEach(function(chat) {
            var photoURL = "";
            console.log(chat.user);
            // push the chat info so later we can load messages
            // by its ID
            list.push({
              id: chat.user.id,
              name: chat.user.name
            });
          });
          // okay, now that we are done looping lets add photos
          // to each one of those so it looks better
          list.forEach(function(listItem, i) {
            User.findOne({_id: listItem.id}, 'photo', function(err, guest) { 
              listItem.photo = guest.photo;
              // if this is the last one send json object back
              if (i+1 == list.length) {
                res.json({list: list});
              }
            });
          });
        }
      });
    });