概念简析:
Node.js是单线程的,除了系统IO之外,在它的事件轮询过程中,同一时间只会处理一个事件。你可以把事件轮询想象成一个大的队列,在每个时间点上,系统只会处理一个事件。即使你的电脑有多个CPU核心,你也无法同时并行的处理多个事件。但也就是这种特性使得node.js适合处理I/O型的应用,不适合那种CPU运算型的应用。在每个I/O型的应用中,你只需要给每一个输入输出定义一个回调函数即可,他们会自动加入到事件轮询的处理队列里。当I/O操作完成后,这个回调函数会被触发。然后系统会继续处理其他的请求。
process.nextTick()的意思就是定义出一个动作,并且让这个动作 在下一个事件轮询的时间点上执行
案例:
function foo() {
console.error('foo');
}
process.nextTick(foo);
console.error('bar');
输出:
bar
foo
使用场景
在多个事件里交叉执行CPU运算密集型的任务
var http = require('http');
function compute() {
// performs complicated calculations continuously
// ...
process.nextTick(compute);
}
http.createServer(function(req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World');
}).listen(5000, '127.0.0.1');
compute();
保持回调函数异步执行的原则
var client = net.connect(8124, function() {
console.log('client connected');
client.write('world!\r\n');
});
在上面的代码里,如果因为某种原因,net.connect()变成同步执行的了,
回调函数就会被立刻执行,因此回调函数写到客户端的变量就永远不会被初始化了。
这种情况下我们就可以使用process.nextTick()把上面asyncFake()改成异步执行的:
function asyncReal(data, callback) {
process.nextTick(function() {
callback(data === 'foo');
});
}
用在事件触发过程中
来看一个例子,你想写一个库实现这样的功能:从源文件里读取数据,
当读取完毕后,触发一个事件同时传递读取的数据。可能你会这样写:
var EventEmitter = require('events').EventEmitter;
function StreamLibrary(resourceName) {
this.emit('start');
// read from the file, and for every chunk read, do:
this.emit('data', chunkRead);
}
StreamLibrary.prototype.__proto__ = EventEmitter.prototype;// inherit from EventEmitter
下面是一段调用这个库的客户端程序,我们想在程序中监听这些事件:
var stream = new StreamLibrary('fooResource');
stream.on('start', function() {
console.log('Reading has started');
});
stream.on('data', function(chunk) {
console.log('Received: ' + chunk);
});
但是上面的代码中,将永远接收不到“start”事件,因为在这个库实例化的时候,
“start”事件会被立刻触发执行,但此时事件的回调函数还没有准备好,
所以在客户端根本无法接收到这个事件。同样,我们可以用process.nextTick()
来改写事件触发的过程,下面是一个正确的版本:
function StreamLibrary(resourceName) {
var self = this;
process.nextTick(function() {
self.emit('start');
});
// read from the file, and for every chunk read, do:
this.emit('data', chunkRead);
}