简介
触发器(Emitter)类型的对象会触发命名事件来调用函数(又称监听器,Listener)。所有能触发事件的对象都是 EventEmitter 类的实例。 这些对象有一个 eventEmitter.on() 函数,用于将一个或多个函数绑定到命名事件上。 事件的命名通常是驼峰式的字符串。
当 EventEmitter 对象触发一个事件时,所有绑定在该事件上的函数都会被同步地调用。 被调用的监听器返回的任何值都将会被忽略并丢弃。
例子,一个简单的 EventEmitter 实例,绑定了一个监听器。 eventEmitter.on() 用于注册监听器, eventEmitter.emit() 用于触发事件。使用 eventEmitter.once() 可以注册最多可调用一次的监听器。 当事件被触发时,监听器会被注销,然后再调用。
const EventEmitter = require('events'); class MyEmitter extends EventEmitter {} const myEmitter = new MyEmitter(); myEmitter.on('event', () => { console.log('触发事件'); }); myEmitter.emit('event');
将参数和 this 传给监听器
eventEmitter.emit() 方法可以传任意数量的参数到监听器函数。 当监听器函数被调用时, this 关键词会被指向监听器所绑定的 EventEmitter 实例。
const myEmitter = new MyEmitter(); myEmitter.on('event', function(a, b) { console.log(a, b, this, this === myEmitter); }); myEmitter.emit('event', 'a', 'b');
也可以使用 ES6 的箭头函数作为监听器。但 this 关键词不会指向 EventEmitter 实例:
异步 VS 同步
EventEmitter 以注册的顺序同步地调用所有监听器。 这样可以确保事件的正确排序,并有助于避免竞态条件和逻辑错误。 当适当时,监听器函数可以使用 setImmediate() 和 process.nextTick() 方法切换到异步的操作模式:
const myEmitter = new MyEmitter(); myEmitter.on('event', (a, b) => { setImmediate(() => { console.log('异步地发生'); }); }); myEmitter.emit('event', 'a', 'b');
错误事件
当 EventEmitter 实例出错时,应该触发 'error' 事件。 这些在 Node.js 中被视为特殊情况。
如果没有为 'error' 事件注册监听器,则当 'error' 事件触发时,会抛出错误、打印堆栈跟踪、并退出 Node.js 进程。
const myEmitter = new MyEmitter(); myEmitter.emit('error', new Error('错误信息')); // 抛出错误并使 Node.js 崩溃。
为了防止崩溃 Node.js 进程,可以使用 domain 模块。 (但请注意,不推荐使用 domain 模块。)
作为最佳实践,应该始终为 'error' 事件注册监听器
const myEmitter = new MyEmitter(); myEmitter.on('error', (err) => { console.error('错误信息'); }); myEmitter.emit('error', new Error('错误')); // 打印: 错误信息
通过使用符号 errorMonitor 安装监听器,可以监视 'error' 事件但不消耗触发的错误。
const myEmitter = new MyEmitter(); myEmitter.on(EventEmitter.errorMonitor, (err) => { MyMonitoringTool.log(err); }); myEmitter.emit('error', new Error('错误')); // 仍然抛出错误并使 Node.js 崩溃。
捕捉 Promise 的拒绝
const ee = new EventEmitter(); ee.on('something', async (value) => { throw new Error('kaboom'); });
内存管理与防止泄漏
在绑定事件监听器的时候,如果监听器没有被 remove,那么存在内存泄漏的风险。
参考文档:
http://nodejs.cn/api/events.html