Node.js 事件,events 模块 和 EventEmitter 类
事件循环
Node.js 是单进程单线程应用程序,但是因为 V8 引擎提供的异步执行回调接口,通过这些接口可以处理大量的并发,所以性能非常高。
Node.js 几乎每一个 API 都是支持回调函数的。
Node.js 基本上所有的事件机制都是用设计模式中观察者模式实现。
Node.js 单线程类似进入一个while(true)的事件循环,直到没有事件观察者退出,每个异步事件都生成一个事件观察者,如果有事件发生就调用该回调函数.
事件驱动程序
Node.js 使用事件驱动模型,当web server接收到请求,就把它关闭然后进行处理,然后去服务下一个web请求。
当这个请求完成,它被放回处理队列,当到达队列开头,这个结果被返回给用户。
这个模型非常高效可扩展性非常强,因为 webserver 一直接受请求而不等待任何读写操作。(这也称之为非阻塞式IO或者事件驱动IO)
在事件驱动模型中,会生成一个主循环来监听事件,当检测到事件时触发回调函数。
整个事件驱动的流程就是这么实现的,非常简洁。有点类似于观察者模式,事件相当于一个主题(Subject),而所有注册到这个事件上的处理函数相当于观察者(Observer)。
基于内置events模块的事件处理方法
Node.js 有多个内置的事件,我们可以通过引入 events 模块,并通过实例化 EventEmitter 类来绑定和监听事件,如下实例:
# 引入events事件模块
var events = require('events');
# 创建事件触发器对象eventsEmitter
var eventEmitter = new events.EventEmitter();
# 绑定事件和事件的处理程序
eventsEmitter.on('eventName',eventHandler);
# 通过程序触发事件
eventEmitter.emit('eventName');
示例
# main.js
// 引入 events 模块
var events = require('events');
// 创建 eventEmitter 对象
var eventEmitter = new events.EventEmitter();
// 创建事件处理程序
var connectHandler = function connected() {
console.log('连接成功。');
// 触发 data_received 事件
eventEmitter.emit('data_received');
}
// 绑定 connection 事件处理程序
eventEmitter.on('connection', connectHandler);
// 使用匿名函数绑定 data_received 事件
eventEmitter.on('data_received', function(){
console.log('数据接收成功。');
});
// 触发 connection 事件
eventEmitter.emit('connection');
console.log("程序执行完毕。");
Node.js EventEmitter类
Nodejs所有的异步I/O操作在完成时都会发送一个事件到事件队列
Nodejs的许多对象都会分发事件,如:
一个net.Server对象会在每次有新连接时触发一个事件
一个fs.readStream对象会在文件被打开时触发一个事件
所有这些产生事件的对象都是events.EventEmitter的实例
// 引入 events 模块
var events = require('events');
// 创建 eventEmitter 对象
// events 模块只提供了EventEmitter这一个对象,核心就是事件触发与事件监听器功能的封装
var eventEmitter = new events.EventEmitter();
EventEmitter 对象如果在实例化时发生错误,会触发 error 事件。当添加新的监听器时,newListener 事件会触发,当监听器被移除时,removeListener 事件被触发。
# events模块文档
# 其他方法
# event表示字符串格式的事件名称
# listener表示一个回调函数
// 添加监听器到监听器数组的尾部
.addListener(event, listener)
// 为指定事件注册监听器
.on(event, listener)
// 单次监听器,出发后解除该监听器
.once(event,listener)
// 移除监听器
.removeListener(event, listener)
// 移除所有监听器
.removeAllListeners([event])
// 设置监听器最大数量(默认为10,超出会警告)
.setMaxListeners(n)
// 返回指定监听器监听器列表
.listeners(event)
// 按监听器顺序执行每个监听器
.emit(event, [arg1], [arg2], [...])
# 类方法(静态方法)
// 返回事件监听器数量
events.emitter.listenerCount(eventName)
# 事件(模块自带的)
// 该事件在添加新监听器时被触发
newListener
// 从指定监听器数组中删除一个监听器。需要注意的是,此操作将会改变处于被删监听器之后的那些监听器的索引
removeListener
# error 事件
EventEmitter 定义了一个特殊的事件 error,它包含了错误的语义,我们在遇到 异常的时候通常会触发 error 事件。
当 error 被触发时,EventEmitter 规定如果没有响 应的监听器,Node.js 会把它当作异常,退出程序并输出错误信息。
我们一般要为会触发 error 事件的对象设置监听器,避免遇到错误后整个程序崩溃。
# 这段代码因为没有为error设置监听器,所以程序会崩溃
var events = require('events');
var emitter = new events.EventEmitter();
emitter.emit('error');
// 程序崩溃并显示如下信息:
// ···
// throw e; // process.nextTick error, or 'error' event on first tick
// ···
继承 EventEmitter
大多数时候我们不会直接使用 EventEmitter,而是在对象中继承它。
包括 fs、net、 http 在内的,只要是支持事件响应的核心模块都是 EventEmitter 的子类。
为什么要这样做呢?原因有两点:
首先,具有某个实体功能的对象实现事件符合语义, 事件的监听和发生应该是一个对象的方法。
其次 JavaScript 的对象机制是基于原型的,支持 部分多重继承,继承 EventEmitter 不会打乱对象原有的继承关系。