Node Events
夏安 人气:0Node 中的 Events
Node 的 Events 模块只定义了一个类,就是 EventEmitter(以下简称 Event ),这个类在很多 Node 本身以及第三方模块中大量使用,通常是用作基类被继承。
在 Node 中,事件的应用遍及代码的每一个角落。
1. 事件和监听器
Node 程序中的对象会产生一系列的事件,它们被称为事件触发器(event emitter),例如一个 HTTP Server 会在每次有新连接时触发一个事件,一个 Readable Stream 会在文件打开时触发一个事件等。
所有能触发事件的对象都是 EventEmitter
类的实例。EventEmitter
定义了 on
方法,该方法的声明如下:
emitter.on(eventName, listener) eventName <String> | <Symbol> The name of the event. listener <Function> The callback function
on
方法接受两个参数:需要监听的事件的名称,当事件触发时需要调用的函数。因为 EventEmitter
是接口,从 EventEmitter
继承的类需要使用 new
关键字来构造。
触发事件监听器很简单,只要调用 EventEmitter
实例的 emit
方法就行了。需要注意的是,这些事件是针对某个实例的,不存在全局的事件。当你调用 on
方法的时候,需要绑定在特定的基于 EventEmitter
的对象上。EventEmitter
类不同的实例之间也不会共享事件。
下面是一个事件注册和触发事件的例子。
const eventEmitter = require('events'); const myEmitter = new eventEmitter(); myEmitter.on('begin', () => { console.log('begin'); }); myEmitter.emit('begin');
上面的代码中,首先初始化了一个 EventEmitter
实例,然后注册了一个名为 begin
的事件,之后调用 emit
方法触发了这一事件。
用户可以注册多个同名的事件,在上面的例子中,如果注册两个名为 begin
的事件,那么它们都会被触发。
如果想获取当前的 emitter
一共注册了哪些事件,可以使用 eventNames
方法。
console.log(myEmitter.eventNames());
该方法会输出包括全部事件名称的数组。就算注册了两个同名的 event
,输出结果也只有一个,说明该方法的结果集并不包含重复结果。
2. 处理 error 事件
由于 Node 代码运行在单线程环境中,那么运行时出现的任何错误都有可能导致整个进程退出。利用事件机制可以实现简单的错误处理功能。
当 Node 程序出现错误的时候,通常会触发一个错误事件,如果代码中没有注册相应的处理方法,会导致 Node 进程崩溃退出。例如:
myEmitter.emit("error", new Error("crash!"));
上面的代码主动抛出了一个 emor
,相当于:
throw new Error ("crash");
如果我们不想因为抛出一个 error
而使进程退出,那么可以让 uncaughtException
事件作为最后一道防线来捕获异常。
const eventEmitter = require('events'); const myEmitter = new eventEmitter(); process.on('uncaughtException', () => { console.log('got error'); }); throw new Error('Error occurred');
这种错误处理的方式虽然可以捕获异常,避免了进程的退出,但不值得提倡。
关于其常见的方法如下:
emitter.addListener/on(eventName, listener)
:添加类型为eventName
的监听事件到事件数组尾部emitter.prependListener(eventName, listener)
:添加类型为eventName
的监听事件到事件数组头部emitter.emit(eventName[, ...args])
:触发类型为eventName
的监听事件emitter.removeListener/off(eventName, listener)
:移除类型为eventName
的监听事件emitter.once(eventName, listener)
:添加类型为eventName
的监听事件,以后只能执行一次并删除emitter.removeAllListeners([eventName])
: 移除全部类型为eventName
的监听事件
3. 继承 Events 模块
在实际的开发中,通常不会直接使用 Event 模块来进行事件处理,而是选择将其作为基类进行继承的方式来使用 Event,在 Node 的内部实现中,凡是提供了事件机制的模块,都会在内部继承 Event 模块。
4. 手写 EventEmitter
下面我们来看看如何手写一个 EventEmitter
。
class EventEmitter { constructor() { this.events = {}; } on(type, handler) { if (!this.events[type]) { this.events[type] = []; } this.events[type].push(handler); } addListener(type, handler) { this.on(type, handler) } prependListener(type, handler) { if (!this.events[type]) { this.events[type] = []; } this.events[type].unshift(handler); } removeListener(type, handler) { if (!this.events[type]) { return; } this.events[type] = this.events[type].filter(item => item !== handler); } off(type, handler) { this.removeListener(type, handler) } emit(type, ...args) { this.events[type].forEach((item) => { Reflect.apply(item, this, args); }); } once(type, handler) { this.on(type, this._onceWrap(type, handler, this)); } _onceWrap(type, handler, target) { const state = { fired: false, handler, type, target }; const wrapFn = this._onceWrapper.bind(state); state.wrapFn = wrapFn; return wrapFn; } _onceWrapper(...args) { if (!this.fired) { this.fired = true; Reflect.apply(this.handler, this.target, args); this.target.off(this.type, this.wrapFn); } } }
加载全部内容