node.js - 按时附加事件处理程序

时间:2012-07-23 14:06:26

标签: node.js javascript-events

我正在研究node.js并在node.js手册中遇到了这个例子:

...
var req = http.request(options);
req.end();

req.on('upgrade', function(res, socket, upgradeHead) {
  console.log('got upgraded!');
  socket.end();
  process.exit(0);
});
...

我在这个例子中看到的是附加到HTTP请求事件的处理程序,在创建请求之后,甚至在发送(计划发送)之后 。更糟糕的是,手册说:

  

如果未收听此事件,则接收升级标头的客户端将关闭其连接。

req.on(...有机会附加处理程序之前,是否有可能发生?我怀疑我对节点异步模型中的某些内容并不了解。或者这个代码来自节点手册,希望网络请求比执行下一行代码需要更长的时间?!

另一个例子:

http.get("http://www.google.com/index.html", function(res) {
  console.log("Got response: " + res.statusCode);
}).on('error', function(e) {
  console.log("Got error: " + e.message);
});

这里,HTTP请求将在创建对象后立即启动,我们之后才会附加错误处理程序。同样,(1)它是一个仅仅因为而有效的代码 网络延迟,(2)我没有得到关于node.js概念的内容,或者(2b)事件将“等待”,直到我将处理程序附加到它?

修改: 更好的例子,也来自手册。下面的 Bad 示例之所以不同,只是因为在好的情况下,我们足够快地附加事件,因此低机会会错过数据,或者这种方式永远不可能错过数据(为什么?!)

// Good
request.on('response', function (response) {
  response.on('data', function (chunk) {
    console.log('BODY: ' + chunk);
  });
});

// Bad - misses all or part of the body
request.on('response', function (response) {
  setTimeout(function () {
    response.on('data', function (chunk) {
      console.log('BODY: ' + chunk);
    });
  }, 10);
});

2 个答案:

答案 0 :(得分:6)

你想念的是JavaScript根本不是异步的!我的意思是JavaScript是单线程的,异步操作实际上并不是异步的。有一个非常奇特的队列模型,它给出了JavaScript异步的错觉(不要误解我: 是最有效的模型)。

那是什么意思?这意味着一旦同步代码运行,就不可能其他代码并行运行。例如,在此代码中

var req = http.request(options);
req.end();
req.on(...);

请求已被调度,但主线程(即操作)尚未在req.end()结束。只要主操作没有完成,就不会发生异步代码。特别是在实际事件有可能发生之前,处理程序始终设置。

又一个让它更清晰的例子。请考虑以下代码:

var http = require('http');
http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World\n');
    while(true) { } // <------ infinite loop doing nothing
}).listen(1337, '127.0.0.1');

请注意,第一个请求将成功完成。但任何其他请求永远不会得到回复。这是因为循环永远不会完成操作,而JavaScript 不能跳转到另一个事件。这段代码永远崩溃了应用程序,超出了任何希望。所以要小心使用Node.js的同步代码。 :)

答案 1 :(得分:3)

要理解的关键是,在调用异步函数时,只有一个事件循环和控制只会留下事件循环的当前“滴答”。通常,这会在您执行I / O(每几行代码很常见)或调用setTimeout / setInterval(相当罕见)时自然发生。因此,只要所有事件处理程序都在事件循环的相同标记内注册,您就永远不会丢失任何数据。而且,在事件循环的那个标记内,你附加处理程序的顺序并不重要,因为在那个标记中你的代码实际上是节点执行的唯一东西,因此没有其他代码可以接收I / O或调用任何事件处理程序,直到当前事件循环tick完成。它不是等待“足够长”或网络延迟或类似的东西。这是单个事件循环的简单性,可以保证关于事件处理函数的可预测操作。