Node.js
事件循环
107
最后,我们监听主机
3620
端口,开始接收连接:
serverResponse.end('Hello, world!\n')
}
var httpServer = http.createServer(serverRequestHandler)
httpServer.listen(3620, '127.0.0.1')
console.log('Server running at http://127.0.0.1:3620.')
Let’s break this example down. First, we import the http model:
var http = require('http')
Next up are the serverRequestHandler and endServerResponse functions:
function serverRequestHandler (serverRequest, serverResponse) {
serverResponse.writeHead(200, {'content-type': 'text/plain'})
endServerResponse(serverResponse)
}
function endServerResponse(serverResponse) {
serverResponse.end('Hello, world!\n')
}
serverRequestHandler is the callback for handling requests to our server. When called,
it will be passed a “request” object and a “response” object. The request object contains
all the necessary data about the current request and provides facilities for accessing
that data. The response object provides facilities for constructing and sending respon-
ses. One interesting thing to note here is that
serverRequestHandler calls endServerRes
ponse
. This is interesting because when serverRequestHandler is called, it won’t be run-
ning in the same environment it was defined in.
endServerResponse shouldn’t be avail-
able, but because of closures in JavaScript, all of the state available to
serverRequestHan
dler
where it was defined will continue to be available to it no matter where it is
called.
Next, we create a new server and pass in the handler to use for requests. When we
express handler will be cached, and every time a request is sent to our server it will be
pushed onto the queue of callbacks to call:
var httpServer = http.createServer(serverRequestHandler)
Finally, we express our interest to begin accepting connections on the port 3620 and
hostname 127.0.0.1:
httpServer.listen(3620, '127.0.0.1')
This is the most important piece of the code. By expressing this interest, the system
will start watching for requests, triggering the appropriate events when necessary and
invoking the necessary callbacks.
CHAPTER TEN: THE NODE.JS EVENT LOOP
98
上面这行代码最为重要。执行完该行代码,系统开始监听请求,必要时即时触发合
适的事件,调用必要的回调函数。
并发
Node.js
是单线程。我们讨论这句话是什么意思之前,先讨论并发
Concurrency
)。
很多人似乎对并发理解有误,他们误以为并发跟并行(
Parallelism
)完全相同。两
者虽有联系,但并不相同。
并发是指一组任务可在重叠的时间段内开始、运行和结束。这些任务也许从未同时
运行,但它们实际上可以这样做。并行是指一组任务在同一时刻运行。
当我说
Node.js
是单线程,指的是
Node.js
事件循环在任意时间点最多只能管理一个
线程,当然也就只有一个调用栈。按照同样逻辑,事件循环只能同时处理一个请求,
这一点很重要,需注意。
因此,虽然能用
Node.js
编写高并发的服务器,但服务器一次只能处理一个请求。
该特性虽难以理解但却非常重要,若考虑用
Node.js
实现并发,需注意这一点。
我们结合上节的
HTTP
服务器示例来理解我所讲的并发到底是什么意思。当一个请
求发送至服务器,“请求”事件被触发,获取到请求数据,请求处理函数被加入任
务队列。当调用栈空闲,事件循环无事可做时,将调用请求处理函数。服务器能够
并发地处理大量请求,因为每个请求都被快速、独立处理,因而未造成阻断。
为事件循环增加任务
假如我们想让事件循环多做一点工作。有没有高效的方法?有!
process.nextTick 是我们的救星!你可以为
Node.js
事件循环加入回调函数,下次
循环可立即调用它:
Concurrency
Node.js is single-threaded. Before I talk about what that means, let’s talk about con-
currency. It seems a lot of folks get the wrong idea about concurrency, assuming that
it’s exactly the same thing as parallelism. The terms are actually related, but they’re
not the same.
Concurrency is when a set of tasks can start, run, and complete in overlapping time
periods. The tasks may never run at the same time, but they could. Parallelism, on the
other hand, is when a set of tasks are running at the same time.
When I say that Node.js is single-threaded, what I mean is that the Node.js event loop is
managing at most one thread at any point in time, which of course means a single call
stack. By that same logic, an important thing to note is that the event loop can only
ever do one thing at once.
So while you’re able to write highly concurrent servers with Node.js, your servers can
process only one request at a time. This is a difficult but important distinction to
understand when thinking about Node.js concurrency.
Let’s use the earlier HTTP server example to understand what I mean by concurrency
here. When a request comes to our server, the “request” event is triggered with the
request data, which causes our request handler to be pushed onto the task queue.
Once the call stack is free and the event loop is free of things to process, our request
handler will be invoked. The server is able to handle many requests concurrently,
because every request is processed quickly and independently without blocking.
Adding Tasks to the Event Loop
So let’s say you want to give the event loop a little bit of work to do. Is there a way to
do that efficiently? Yes!
process.nextTick to the rescue! process.nextTick enables you to provide the Node.js
event loop a callback to invoke immediately in the next iteration, or tick, of the event
loop:
function runCPUIntensiveTask(data) {
if (data === null) {
return
}
// Do some CPU-intensive work ...
process.nextTick(function () {
runCPUIntensiveTask(newData)
})
}
CONCURRENCY
99

Get JavaScript 之美 now with O’Reilly online learning.

O’Reilly members experience live online training, plus books, videos, and digital content from 200+ publishers.