但在内部的处理机制上,process.nextTick()和setTimeout(fn, 0)是不同的,process.nextTick()不是一个单纯的延时,他有更多的特性。
更精确的说,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的一点区别
Module.exports才是真正的接口,exports只不过是它的一个辅助工具。最终返回给调用的是Module.exports而不是exports。
所有的exports收集到的属性和方法,都赋值给了Module.exports。当然,这有个前提,就是Module.exports本身不具备任何属性和方法。
如果,Module.exports已经具备一些属性和方法,那么exports收集来的信息将被忽略。
举个栗子:
新建一个文件 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'