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

我正在学习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请求的事件,请求被创build后,甚至被发送(预定)之后 。 为了让事情变得更糟,手册说:

如果这个事件没有被监听,那么接收到升级头的客户端将closures它们的连接。

req.on(... 之前是不是有可能发生事件req.on(...有机会附加一个处理程序?我怀疑我不明白在node的asynchronous模型中有什么东西,或者这个代码是从node中手动devise的,希望networking请求将花费比执行下一行代码更长的时间?

另一个例子:

 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请求将在创build对象后立即启动,并且仅在之后才附加error handling程序。 再说一遍,(1)它是一种仅仅因为networking延迟而起作用的代码,(2)我没有得到关于node.js概念的东西,或者(2b)事件会“等待”,直到我附加一个处理程序为止。

编辑 :甚至更好的例子,也从手册。 下面的好的坏的例子是不同的,因为在好的例子中,我们附加事件足够快,因此错过数据的机会很less ,或者这样不可能错过数据 (为什么?)

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

你错过的是JavaScript不是asynchronous的! 我的意思是JavaScript是单线程的,asynchronous操作实际上不是asynchronous的。 有一个非常奇特的队列模型,它给出了JavaScriptasynchronous的错觉(不要误解我的意思:它仍然最有效的模型)。

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

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

请求被调度,但是主线程(即操作)还没有在req.end()结束。 只要主操作没有完成,就不会有asynchronous代码在中间触发。 特别是处理程序总是在实际事件有机会发生之前设置。

还有一个例子使它更清晰。 考虑这个代码:

 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的同步代码。 🙂

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