NodeJS routing -

NodeJS routing -

本文关键字:routing NodeJS      更新时间:2023-09-26

摘要:

我调用了下面的代码一次,下次调用时一切都很好。我在代码下面得到了错误,然后我需要重新启动服务器才能使其工作,但同样,它只工作一次。

注意:

我使用eventEmitter的原因是,如果我用mongoose嵌套两个db请求,它将先做parent,然后做child,所以我必须确保第一个请求完成,才能运行第二个请求。但这不是代码工作的问题,而是我第一次运行它。以前从未遇到过这个问题,我真的很好奇发生了什么

代码:

// 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)

事实上,我通过重写代码并将照片草签添加到聊天列表对象中来解决了这个问题,这样我就不必手动添加照片了,这是一个更快、更聪明的解决方案,但我真的很好奇为什么这不起作用,点赞或分享这个问题,让我们都能发现,干杯!

将事件发射器从路由处理程序中拉出。每次运行路由处理程序时,都会为'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.on更改为eventEmitter.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});
          }
        });
      });
    }
  });
});