node.js-v0.8API解读(1)-domain

node.js的0.8版本stable版本已经发布了,官方解释性能有大幅提升,而且稳定性也大幅增强。看了官方给出的数据,性能大约有30%-50%的提升,还是很给力的!以下是官方给出的node-v0.8版本的优势

1、Node got a lot faster.

2、Node got more stable.

3、You can do stuff with file descriptors again.

4、The cluster module is much more awesome.

5、The domain module was added.

6、The repl is better.

7、The build system changed from waf to gyp.

8、Some other stuff changed, too.

9、Scroll to the bottom for the links to install it.

api新增了一些,我们来看下新增的一个模块domain的作用。

官方api对domain模块的定义如下:

domain模块提供一种方式用来将不同的IO操作归总到一个组中。如果任何注册的时间触发器或者回调函数触发了error事件或者throw了error对象,domain对象将会被通知到。

domain模块不会自动加载,需要手动的加载它。并且domain模块会在将来的node.js版本中有比较明显的改变和升级。

至此我们基本明白了domain模块的功能,下面我来看下domain模块的各个属性以及方法。

domain.create()

创建一个domain对象,将返回一个新的domain对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var d = domain.create();
d.on('error', function(er) {
console.error('Caught error!', er);
});
d.run(function() {
process.nextTick(function() {
setTimeout(function() { // simulating some various async stuff
fs.open('non-existent file', 'r', function(er, fd) {
if (er) throw er;
// proceed...
});
}, 100);
});
});

以上是官方的一个例子,很明显当匿名函数throw er后,将会被d.on(‘error’,function(er){})捕获,并做处理。

看到这里我们认为我们只需在d.on(‘error’);函数内统一做出错误处理即可,而不必每次都在回调函数中写错误处理了。

但是我在这里不推荐这个方式,因为domain模块接下来有更好的处理方式

domain.bind和domain.intercept 方法:

这2个方法提供了统一处理一类错误机制的可能,而且相当便捷,我们看下官方代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var d = domain.create();

function readSomeFile(filename, cb) {
fs.readFile(filename, d.intercept(function(data) {
// note, the first argument is never passed to the
// callback since it is assumed to be the 'Error' argument
// and thus intercepted by the domain.

// if this throws, it will also be passed to the domain
// so the error-handling logic can be moved to the 'error'
// event on the domain instead of being repeated throughout
// the program.
return cb(null, JSON.parse(data));
}));
}

d.on('error', function(er) {
// an error occurred somewhere.
// if we throw it now, it will crash the program
// with the normal line number and stack message.
});

一个fs.readFile方法,读取文件功能,如果出错则转交给d.on(‘error’)方法去处理,如果成功则继续我们的流程。

所以可以这样理解,如果出错了,则cb是不会执行的,而只会执行d.on(‘error’)方法内的回调函数。这样我们省去了很多麻烦,以前我们往往这样写代码:

1
2
3
4
5
6
7
8
function readSomeFile(filename, cb) {
fs.readFile(filename, function(err, data) {
if(err) return errorHandler(err)&&cb(err);
else cb(null, data);
});
}

function errorHandle(err){ console.error(err); }

加入了domain模块我们就可以删除这些if判断了,统一由domain模块来管理err情况,让我们代码更清晰,调理更清楚,也省去了不少麻烦。

为了方便我们管理domain对象,domain对象还提供了一些其他的属性和方法如下:

domain.run(fn)

创建一个domain上下文,在上下文中抛出的error或者触发器触发的error事件都将被domain.on(‘error’)接收到,一方面统一管理,另一方面也避免了因为错误将整个node.js进程搞挂掉。

domain.members

一个数组用来存放注册到domain模块的定时器和事件触发器

domain.add(emitter)

注册一个定时器或者时间触发器,当定时器或者事件触发器抛出异常,则会被domain模块捕获

domain.remove(emitter)

删除一个定时器或者事件触发器

domain.bind(fn)

和上面的示例domain.intercept一样,只是bind方法将会把err传递进fn

domain.dispose()

摧毁一个domain对象,node.js进程将尽最大可能回收这部分资源。注意,当还有bind的定时器没有触发前调用dispose方法,定时器的回调函数将不会执行,而且当有IO操作时,IO操作的回调也不会执行。

总结一下

node.js的新domain模块确实解决了很多重复代码的工作,在开发过程中我建议将domain模块进行封装,单独写一个模块用来管理各种不同的错误处理,减少domain.create(),尽量使用1个或几个domain对象来处理整个应用的错误,便于管理