Express.js - 在发送响应之前循环

Express.js - while loop before sending response

我正在尝试在 node.js 中实施现有解决方案,特别是使用 express.js 框架。现在,现有解决方案的工作原理如下:

所以基本上发生的事情是,客户端连接并且连接处于活动状态(在循环中)直到有足够的客户端连接,然后并且只有在那时才会有响应(同时对所有客户端)。

现在我不是这些架构的专家,但在我看来,这不是一个正确或好的解决方案。我最初的想法是:这个必须用套接字来解决。然而,由于现有的解决方案是这样工作的(它不是用 node.js 编写的),我试图模仿这种行为:

var number = (function(){
var count = 0;
  return {
    increase: function() {
        count++;
    },
    get: function(){
        return count;
    }
  };
})();

app.get('/test', function(req, res){
  number.increase();
  while (number.get() < 3) {
  //hold it here, until enough clients connect  
  }
  res.json(number.get());
});

虽然我认为这不是一个正确的解决方案,但我有几个问题:

  1. 除了使用套接字之外,还有其他方法可以解决这个问题吗?
  2. 为什么这个 "logic" 在 C# 中有效,但在 express.js 中无效?上面的代码挂起,没有处理其他请求。
  3. 我知道 node.js 是单线程的,但是如果我们有一个更常规的立即响应的服务,并且同时有 20 个请求怎么办?
  1. 您的 http 方法应该有效
  2. 您正在阻塞事件循环,因此节点在 while 循环中拒绝做任何其他工作
  3. 你真的很接近,你只需要时不时地检查一下,而不是经常检查。我在下面使用 process.nextTick() 执行此操作,但 setTimeout() 也可以:

var number = (function(){
var count = 0;
  return {
    increase: function() {
        count++;
    },
    get: function(){
        return count;
    }
  };
})();

function waitFor3(callback){
  var n = number.get();
  if(n < 3){
    setImmediate(function(){
      waitFor3(callback)
    })
  } else {
    callback(n)
  }
}

function bump(){
  number.increase();
  console.log('waiting');
  waitFor3(function(){
    console.log('done');
  })
}

setInterval(bump, 2000);
/*
app.get('/test', function(req, res){
  number.increase();
  waitFor3(function(){
    res.json(number.get());
  })
});
*/

我可能会为此使用事件发射器:

var EventEmitter = require('events').EventEmitter;
var emitter      = new EventEmitter();

app.get('/', function(req, res) {
  // Increase the number
  number.increase();

  // Get the current value
  var current = number.get();

  // If it's less than 3, wait for the event emitter to trigger.
  if (current < 3) {
    return emitter.once('got3', function() {
      return res.json(number.get()); 
    });
  }

  // If it's exactly 3, emit the event so we wake up other listeners.
  if (current === 3) {
    emitter.emit('got3');
  }

  // Fall through.
  return res.json(current);
});

我想强调的是,@Plato 的说法是正确的,当响应花费太多时间才能完成时,浏览器可能会超时。

编辑:顺便说一句,关于return emitter.once(...)的一些解释。

上面的代码可以这样重写:

if (current < 3) {
  emitter.once('got3', function() {
    res.json(number.get());
  });
} else if (current === 3) {
  emitter.emit('got3');
  res.json(number.get());
} else {
  res.json(number.get());
}

但是我没有使用那些 if/else 语句,而是在创建事件侦听器后从请求处理程序中 return。由于请求处理程序是异步的,因此它们的 return 值将被丢弃,因此您可以 return 任何东西(或什么都不做)。作为替代方案,我也可以使用这个:

if (current < 3) {
  emitter.once(...);
  return;
}
if (current === 3) {
...etc...

此外,即使您从请求处理程序函数 return,事件侦听器仍引用 res 变量,因此请求处理程序范围由 Node 维护,直到 res.json()在事件侦听器回调中被调用。