NodeJs + Express +事件回调后的响应

时间:2014-09-16 10:23:56

标签: node.js express

为了组织代码(&我是一名java开发人员),我将代码分为Routes&服务。

免责声明:我是Node上的无人。

服务代码:

UserService.prototype.findUser = function(userId) {
  var self = this;
  container.db.users.findOne({
    userId : userId
  }, function(err, user) {
    self.emit('userFetched', err, user);
  });
};

路由器代码:

var login = function(req, res) {
  service.on('userFetched', function(err, user) {
    loginAndRedirect(err, user, req, res);
  });
  service.getUser(req.body.username, req.body.password);
};

当登录请求到来时,我收到错误:

http.js:689
    throw new Error('Can\'t set headers after they are sent.');
          ^
Error: Can't set headers after they are sent.
    at ServerResponse.OutgoingMessage.setHeader (http.js:689:11)
    at ServerResponse.res.setHeader (C:\Users\O603088\Documents\Workspace\Learning\Shory\node_modules\express\node_modules\connect\lib\patch.js:59:22)
    at ServerResponse.res.set.res.header (C:\Users\O603088\Documents\Workspace\Learning\Shory\node_modules\express\lib\response.js:518:10)
    at ServerResponse.res.location (C:\Users\O603088\Documents\Workspace\Learning\Shory\node_modules\express\lib\response.js:652:8)
    at ServerResponse.res.redirect (C:\Users\O603088\Documents\Workspace\Learning\Shory\node_modules\express\lib\response.js:694:8)
    at loginAndRedirect (C:\Users\O603088\Documents\Workspace\Learning\Shory\server\routes\auth.js:24:9)
    at UserService.<anonymous> (C:\Users\O603088\Documents\Workspace\Learning\Shory\server\routes\auth.js:14:5)
    at UserService.EventEmitter.emit (events.js:117:20)
    at C:\Users\O603088\Documents\Workspace\Learning\Shory\server\services\UserService.js:19:10
    at callback (C:\Users\O603088\Documents\Workspace\Learning\Shory\node_modules\nedb\lib\executor.js:26:17)

我认为其中一个原因是登录功能在回调执行之前结束,因此响应已经生成。因此,当执行重定向时,抛出about错误。

然而,这有效:

var login = function(req, res) {
  service.getUser(req.body.username, req.body.password).on('userFetched',
      function(err, user) {
        loginAndRedirect(err, user, req, res);
      });
};

UserService.prototype.getUser = function(username, password) {
  var self = this;
  container.db.users.findOne({
    username : username,
    password : password
  }, function(err, user){
    self.emit("userFetched", err, user);
  });
  return this; // Added this for method chaining
};
  1. 为什么我在第一种技术中遇到错误?
  2. 第二种方法总能奏效吗?或者这只是一个巧合吗?
  3. 我知道这样做的万无一失的方法是将一个回调函数传递给findUser方法并从findUser方法中调用它,但我不太喜欢这种方法。

2 个答案:

答案 0 :(得分:1)

您的错误显示:

 throw new Error('Can\'t set headers after they are sent.');

意味着您试图将响应一次发送回另一个。

可能在您的代码中:

var login = function(req, res) {
  service.on('userFetched', function(err, user) {
    loginAndRedirect(err, user, req, res);-----------------------> 1st response sent
  });
  service.getUser(req.body.username, req.body.password);-------------> 2nd response sent.
};

检查您是否同时发送回复..

答案 1 :(得分:1)

  
    

第二种方法总能奏效吗?或者这只是一个巧合吗?

  

我认为这是巧合。方法是一样的。您遇到问题的原因是您遇到并发问题。如果您一次只发出一个请求,这将正常工作。如果您有多个重叠的请求,则可能会遇到此问题。

我认为问题在于这一行:

service.on('userFetched', function(err, user) {
  loginAndRedirect(err, user, req, res);
});

这种方法的问题在于,当发出任何 userFetched事件时,将触发回调,而不仅仅是与您有关闭的请求相对应的事件。当您致电.on时,您正在为UserService提供对您的回调的引用,并保留该引用。事实上,它将永远保持下去。

因此,请求1进来,询问用户,然后侦听userFetched事件,接收事件,并将数据发送到响应1.到目前为止一直很好。

然后请求2进入,请求用户,然后侦听userFetched事件。请求1的回调将数据发送到响应1,然后响应2的回调将数据发送到响应2.第一部分将抛出错误(您在上面报告的错误)。根据错误处理的设置方式,响应2部分可能会或可能不会实际发生。

由于每个请求都添加了一个事件监听器并且永远不会将其删除,因此最终您的堆将填满事件监听器,所有这些监听器都会在请求和响应对象周围关闭,从而防止它们被垃圾回收。

我的猜测是,如果你运行这个程序的时间足够长,你将获得相当于OutOfMemoryError的JavaScript。

  
    

我知道这样做的傻瓜式方法是将一个回调函数传递给findUser方法并从findUser方法中调用它,但我不太喜欢这种方法。

  

如果你正在努力解决一种无法发挥作用的方式并且知道一种有效的方法,并且这是一种记录的,已知的,良好的方法,那你为什么要与它作斗争呢?一次性回调正是解决此问题的方法。你通过战斗来改善你的生活。