[url=https://gist.github.com/1257394]特性[/url]。
更精确的说,process.nextTick()定义的调用会创建一个新的子堆栈。在当前的栈里,你可以执行任意多的操作。但一旦调用netxTick,函数就必须返回到父堆栈。然后事件轮询机制又重新等待处理新的事件,如果发现nextTick的调用,就会创建一个新的栈。
下面我们来看看,什么情况下使用process.nextTick():
在多个事件里交叉执行CPU运算密集型的任务:
在下面的例子里有一个compute(),我们希望这个函数尽可能持续的执行,来进行一些运算密集的任务。
但与此同时,我们还希望系统不要被这个函数堵塞住,还需要能响应处理别的事件。这个应用模式就像一个单线程的web服务server。在这里我们就可以使用process.nextTick()来交叉执行compute()和正常的事件响应。
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();
在这种模式下,我们不需要递归的调用compute(),我们只需要在事件循环中使用process.nextTick()定义compute()在下一个时间点执行即可。
在这个过程中,如果有新的http请求进来,事件循环机制会先处理新的请求,然后再调用compute()。
反之,如果你把compute()放在一个递归调用里,那系统就会一直阻塞在compute()里,无法处理新的http请求了。你可以自己试试。
当然,我们无法通过process.nextTick()来获得多CPU下并行执行的真正好处,这只是模拟同一个应用在CPU上分段执行而已。
(2),Console
console {Object} Used to print to stdout and stderr.See the stdio section.
控制台 {对象} 用于打印到标准输出和错误输出。看如下测试:
console.log("Hello Bigbear !") ;
for(var i in console){
console.log(i+" "+console[i]) ;
}
会得到以下输出结果:
var log = function () {
process.stdout.write(format.apply(this, arguments) + '\n');
}
var info = function () {
process.stdout.write(format.apply(this, arguments) + '\n');
}
var warn = function () {
writeError(format.apply(this, arguments) + '\n');
}
var error = function () {
writeError(format.apply(this, arguments) + '\n');
}
var dir = function (object) {
var util = require('util');
process.stdout.write(util.inspect(object) + '\n');
}
var time = function (label) {
times[label] = Date.now();
}
var timeEnd = function (label) {
var duration = Date.now() - times[label];
exports.log('undefined: NaNms', label, duration);
}
var trace = function (label) {
// TODO probably can to do this better with V8's debug object once that is
// exposed.
var err = new Error;
err.name = 'Trace';
err.message = label || '';
Error.captureStackTrace(err, arguments.callee);
console.error(err.stack);
}
var assert = function (expression) {
if (!expression) {
var arr = Array.prototype.slice.call(arguments, 1);
require('assert').ok(false, format.apply(this, arr));
}
}
通过这些函数,我们基本上知道NodeJS在全局作用域添加了些什么内容,其实Console对象上的相关api只是对Process对象上的"stdout.write“进行了更高级的封装挂在到了全局对象上。
(3),exports与module.exports
在NodeJS中,有两种作用域,分为全局作用域和模块作用域
var name = 'var-name';
name = 'name';
global.name='global-name';
this.name = 'module-name';
console.log(global.name);
console.log(this.name);
console.log(name);
我们看到var name = 'var-name';name = 'name'; 是定义的局部变量;
而global.name='global-name';是为 全局对象定义一个name 属性,
而 this.name = 'module-name';是为模块对象定义了一个name 属性
那么我们来验证一下,将下面保存成test2.js,运行
var t1 = require('./test1');
console.log(t1.name);
console.log(global.name);
从结果可以看出,我们成功导入 了test1 模块,并运行了 test1的代码,因为在test2 中 输出 了global.name,
而 t1.name 则是 test1 模块中通过this.name 定义的,说明this 指向 的是 模块作用域对象。
exports与module.exports的一点区别
[code] [/code][code]Module.exports[/code]才是真正的接口,exports只不过是它的一个辅助工具。最终返回给调用的是[code]Module.exports[/code]而不是exports。
所有的exports收集到的属性和方法,都赋值给了[code]Module.exports[/code]。当然,这有个前提,就是[code]Module.exports[/code][code]本身不具备任何属性和方法[/code][code]。[/code]
[code] 如果,[/code][code]Module.exports[/code][code]已经具备一些属性和方法,那么exports收集来的信息将被忽略。[/code]
举个栗子:
新建一个文件 bb.js
exports.name = function() {
console.log('My name is 大熊 !') ;
} ;
创建一个测试文件 test.js
var bb= require('./bb.js');
bb.name(); // 'My name is 大熊 !'
修改bb.js如下:
module.exports = 'BigBear!' ;
exports.name = function() {
console.log('My name is 大熊 !') ;
} ;
再次引用执行bb.js
var bb= require('./bb.js');
bb.name(); // has no method 'name'
由此可知,你的模块并不一定非得返回“实例化对象”。你的模块可以是任何合法的javascript对象--boolean, number, date, JSON, string, function, array等等。
(4),setTimeout,setInterval,process.nextTick,setImmediate
以下以总结的形式出现
Nodejs的特点是事件驱动,异步I/O产生的高并发,产生此特点的引擎是事件循环,事件被分门别类地归到对应的事件观察者上,比如idle观察者,定时器观察者,I/O观察者等等,事件循环每次循环称为Tick,每次Tick按照先后顺序从事件观察者中取出事件进行处理。
调用setTimeout()或setInterval()时创建的计时器会被放入定时器观察者内部的红黑树中,每次Tick时,会从该红黑树中检查定时器是否超过定时时间,超过的话,就立即执行对应的回调函数。setTimeout()和setInterval()都是当定时器使用,他们的区别在于后者是重复触发,而且由于时间设的过短会造成前一次触发后的处理刚完成后一次就紧接着触发。
由于定时器是超时触发,这会导致触发精确度降低,比如用setTimeout设定的超时时间是5秒,当事件循环在第4秒循到了一个任务,它的执行时间3秒的话,那么setTimeout的回调函数就会过期2秒执行,这就是造成精度降低的原因。并且由于采用红黑树和迭代的方式保存定时器和判断触发,较为浪费性能。
使用process.nextTick()所设置的所有回调函数都会放置在数组中,会在下一次Tick时所有的都立即被执行,该操作较为轻量,时间精度高。
setImmediate()设置的回调函数也是在下一次Tick时被调用,其和process.nextTick()的区别在于两点:
1,他们所属的观察者被执行的优先级不一样,process.nextTick()属于idle观察者,setImmediate()属于check观察者,idle的优先级>check。
2,setImmediate()设置的回调函数是放置在一个链表中,每次Tick只执行链表中的一个回调。这是为了保证每次Tick都能快速地被执行。
[b]二,总结一下[/b]
1,理解Global对象存在的意义
2,exports与module.exports的一点区别
3,Console的底层是什么构建的(Process对象的高层封装)
4,setTimeout,setInterval,process.nextTick,setImmediate的区别
5,NodeJS中的两种作用域