2.9 Events
Node的Events模块只定义了一个类,就是EventEmitter(以下简称Event),这个类在很多Node本身以及第三方模块中大量使用,通常是用作基类被继承。
在前面的内容里我们已经接触了很多关于事件处理的概念了,在Node中,事件的应用遍及代码的每一个角落。
2.9.1 事件和监听器
Node程序中的对象会产生一系列的事件,它们被称为事件触发器(event emitter),例如一个HTTP Server会在每次有新连接时触发一个事件,一个Readable Stream会在文件打开时触发一个事件等。
所有能触发事件的对象都是EventEmitter类的实例。EventEmitter定义了on方法,该方法的声明如下:
下面是一个事件注册和触发事件的例子。
代码2.24 注册一个事件并触发它
上面的代码中,首先初始化了一个EventEmitter实例,然后注册了一个名为begin的事件,之后调用emit方法触发了这一事件。
用户可以注册多个同名的事件,在上面的例子中,如果注册两个名为begin的事件,那么它们都会被触发。
如果想获取当前的emitter一共注册了哪些事件,可以使用eventNames方法。
该方法会输出包括全部事件名称的数组。
就算注册了两个同名的event,输出结果也只有一个,说明该方法的结果集并不包含重复结果。
注意:在Node v6.x及之前的版本中,event模块可以通过:
引入,在新的版本中这种写法已经被废弃并会抛出一个异常,只能统一由require进行引入,有时能在一些旧版本的第三方模块中还能看到。
2.9.2 处理error事件
由于Node代码运行在单线程环境中,那么运行时出现的任何错误都有可能导致整个进程退出。利用事件机制可以实现简单的错误处理功能。
当Node程序出现错误的时候,通常会触发一个错误事件,如果代码中没有注册相应的处理方法,会导致Node进程崩溃退出。例如:
上面的代码主动抛出了一个error,相当于:
Node程序会打印出整个错误栈:
如果我们不想因为抛出一个error而使进程退出,那么可以让uncaughtException事件作为最后一道防线来捕获异常。
代码2.25 使用uncaughtException事件捕获异常
这种错误处理的方式虽然可以捕获异常,避免了进程的退出,但不值得提倡,我们会在错误处理一章详细介绍相关的内容。
Event模块还有一些其他的API,这里不再一一介绍。
2.9.3 继承Events模块
在实际的开发中,通常不会直接使用Event模块来进行事件处理,而是选择将其作为基类进行继承的方式来使用Event,在Node的内部实现中,凡是提供了事件机制的模块,都会在内部继承Event模块。
以fs模块为例,下面是其源码中的一部分:
可以看出fs模块通过util.inherit方法继承了Event模块。
假设我们要用Node来开发一个网页上的音乐播放器应用,关于播放和暂停的处理,就可以考虑通过继承events模块来实现。
另一种场景,假设我们想要利用原生的数组来模拟一个消息队列,该队列会在新增消息和弹出消息时触发对应的事件,也可以考虑继承Events模块。
详细的内容可以参考第6章。