目标:通过 demos 对事件驱动架构有个初步的认识
简述:
EventEmitter 在实际工作中使用相对较少,快速将 demos 给过一遍即可。重点在于认识“事件驱动架构”是一种什么样的编程体验,这对于我们学习那些基于 NodeJS 实现的框架而言,帮助是很大的。比如,Electron 就是基于 NodeJS 来实现的,在 Electron 中,用于实现 IPC 通信的模块 ipcMain、ipcRenderer 都是基于 EventEmitter 实现的,了解 EventEmitter 的一些常用 API,有助于我们快速上手 ipcMain、ipcRenderer。
参考资料:
- Node.js Node.js EventEmitter | 菜鸟教程 👉🏻 https://www.runoob.com/nodejs/nodejs-event.html
- 官方文档 events 👉🏻 https://nodejs.org/dist/latest-v18.x/docs/api/events.html#class-eventemitter
概述
EventEmitter 是 Node.js 的核心模块之一,它提供了实现 事件驱动架构 的基本工具。在 Node.js 和基于 Node.js 的应用程序(例如 Electron)中,事件驱动架构是一个核心的设计理念。
- 异步编程:在 Node.js 中,异步编程是常态,EventEmitter 可以让你在某件事情完成(或者发生错误)时触发回调函数。
- 模块间解耦:通过事件,不同的模块可以相互通信,而不需要直接引用对方。
- 实现观察者模式:EventEmitter 提供了一种实现观察者模式的机制,观察者模式是一种设计模式,它定义了对象间的一种一对多的关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。
- 提升应用程序性能:通过使用事件驱动的编程模型,我们可以非阻塞地处理 I/O,这可以让我们的应用程序在等待 I/O(如网络请求或文件读取)完成时做其他事情,从而提高应用程序的性能。
对于 Node.js 和基于 Node.js 的开发者来说,理解和掌握 EventEmitter 是非常重要的。
on、emit、addListener
const {EventEmitter} = require('events')const eIns = new EventEmitter()eIns.on('event', () => {console.log('emit event')})// eIns.addListener('event', () => {// console.log('emit event')// })eIns.emit('event')/*emit event*/
addListener 和 on 是 EventEmitter 类的两个方法,它们在功能上是完全相同的,用于注册事件监听器。实际上,on 方法就是 addListener 方法的别名,它们可以互换使用,没有实质性的区别。我们可以根据个人喜好和代码风格选择使用 addListener 或 on
多次 emit
const {EventEmitter} = require('events')const eIns = new EventEmitter()eIns.on('event', () => {console.log('emit event')})eIns.emit('event')eIns.emit('event')eIns.emit('event')/*emit eventemit eventemit event*/
传递参数
const {EventEmitter} = require('events')const eIns = new EventEmitter()eIns.on('event', (...args) => {console.log('[emit event]:', ...args)})eIns.emit('event', 1)eIns.emit('event', 1, 2)eIns.emit('event', 1, 2, 3)/*[emit event]: 1[emit event]: 1 2[emit event]: 1 2 3*/
once
const {EventEmitter} = require('events')const eIns = new EventEmitter()eIns.once('event', (...args) => {console.log('[emit event]:', ...args)})eIns.emit('event', 1)eIns.emit('event', 1, 2)eIns.emit('event', 1, 2, 3)/*[emit event]: 1*/
off、removeListener
const {EventEmitter} = require('events')const eIns = new EventEmitter()function cb(...args) {console.log('[emit event]:', ...args)}eIns.on('event', cb)eIns.emit('event', 1)eIns.emit('event', 1, 2)eIns.off('event', cb)// eIns.removeListener('event', cb)eIns.emit('event', 1, 2, 3)/*[emit event]: 1[emit event]: 1 2*/
与 on、addListener 一样,off 和 removeListener 也是等效的,根据自己习惯,选择一个喜欢的使用即可。
注意:
- 如果我们在绑定
event事件的监听器,使用的是匿名函数式的写法,那么 removeListener 是没法用的。因为匿名函数没有函数名,这将导致我们没法找到这个函数引用(监听器)。
多次 on
const {EventEmitter} = require('events')const eIns = new EventEmitter()function cb(...args) {console.log('[emit event]:', ...args)}eIns.on('event', cb)eIns.on('event', cb)eIns.on('event', cb)eIns.emit('event', 1)eIns.off('event', cb)eIns.emit('event', 1, 2)eIns.off('event', cb)eIns.emit('event', 1, 2, 3)/*[emit event]: 1[emit event]: 1[emit event]: 1[emit event]: 1 2[emit event]: 1 2[emit event]: 1 2 3*/
listenerCount
const {EventEmitter} = require('events')const eIns = new EventEmitter()function cb(...args) {console.log('[emit event]:', ...args)}eIns.on('event', cb)eIns.on('event', cb)eIns.on('event', cb)console.log(`[eIns.listenerCount('event')]:`, eIns.listenerCount('event'))eIns.emit('event', 1)eIns.removeListener('event', cb)console.log(`[eIns.listenerCount('event')]:`, eIns.listenerCount('event'))eIns.emit('event', 1, 2)eIns.removeListener('event', cb)console.log(`[eIns.listenerCount('event')]:`, eIns.listenerCount('event'))eIns.emit('event', 1, 2, 3)/*[eIns.listenerCount('event')]: 3[emit event]: 1[emit event]: 1[emit event]: 1[eIns.listenerCount('event')]: 2[emit event]: 1 2[emit event]: 1 2[eIns.listenerCount('event')]: 1[emit event]: 1 2 3*/
removeAllListeners
const {EventEmitter} = require('events')const eIns = new EventEmitter()function cb(...args) {console.log('[emit event]:', ...args)}eIns.on('event', cb)eIns.on('event', cb)eIns.on('event', cb)console.log(`[eIns.listenerCount('event')]:`, eIns.listenerCount('event'))eIns.emit('event', 1)eIns.removeListener('event', cb)console.log(`[eIns.listenerCount('event')]:`, eIns.listenerCount('event'))eIns.emit('event', 1, 2)eIns.removeAllListeners('event')console.log(`[eIns.listenerCount('event')]:`, eIns.listenerCount('event'))eIns.emit('event', 1, 2, 3)/*[eIns.listenerCount('event')]: 3[emit event]: 1[emit event]: 1[emit event]: 1[eIns.listenerCount('event')]: 2[emit event]: 1 2[emit event]: 1 2[eIns.listenerCount('event')]: 0*/
const {EventEmitter} = require('events')const eIns = new EventEmitter()eIns.on('event', (...args) => {console.log('[emit event]:', ...args)})eIns.on('event', (...args) => {console.log('[emit event]:', ...args)})eIns.emit('event', 1)eIns.emit('event', 1, 2)eIns.removeAllListeners('event')eIns.emit('event', 1, 2, 3)/*[emit event]: 1[emit event]: 1[emit event]: 1 2[emit event]: 1 2*/
立即执行
const {EventEmitter} = require('events')const eIns = new EventEmitter()eIns.on('event1', () => {console.log('1')eIns.emit('event2')console.log('3')})eIns.on('event2', () => {console.log('2')})eIns.emit('event1')console.log("4")/*1234*/
emit 一个不存在的事件,相当于什么也没做
const {EventEmitter} = require('events')const eIns = new EventEmitter()console.log(1)eIns.emit('event')console.log(2)/*12*/
当我们在 EventEmitter 实例上 emit 一个不存在的事件时,它将静默失败,没有任何影响,不会引发错误或抛出异常。
emit(‘error’)
const {EventEmitter} = require('events')const eIns = new EventEmitter()console.log(1)// eIns.on('error', () => {// // ...// })eIns.emit('error')console.log(2)/*1node:events:504throw err; // Unhandled 'error' event^Error [ERR_UNHANDLED_ERROR]: Unhandled error. (undefined)at new NodeError (node:internal/errors:399:5)at EventEmitter.emit (node:events:502:17)at Object.<anonymous> (/Users/huyouda/0000/0.js:9:6)at Module._compile (node:internal/modules/cjs/loader:1254:14)at Module._extensions..js (node:internal/modules/cjs/loader:1308:10)at Module.load (node:internal/modules/cjs/loader:1117:32)at Module._load (node:internal/modules/cjs/loader:958:12)at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)at node:internal/main/run_main_module:23:47 {code: 'ERR_UNHANDLED_ERROR',context: undefined}Node.js v18.15.0*/
所有的 EventEmitter 实例默认都会注册一个 error 事件监听器。这意味着如果在 EventEmitter 实例中没有显式地注册 error 事件监听器,当触发了一个错误事件且没有对其进行处理时,Node.js 将会打印错误堆栈信息,并可能导致程序退出。
const {EventEmitter} = require('events')const eIns = new EventEmitter()console.log(1)try {eIns.emit('error')} catch (error) {console.error('[emit error event]:', error)}console.log(2)/*1[emit error event]: Error [ERR_UNHANDLED_ERROR]: Unhandled error. (undefined)at new NodeError (node:internal/errors:399:5)at EventEmitter.emit (node:events:502:17)at Object.<anonymous> (/Users/huyouda/0000/0.js:10:8)at Module._compile (node:internal/modules/cjs/loader:1254:14)at Module._extensions..js (node:internal/modules/cjs/loader:1308:10)at Module.load (node:internal/modules/cjs/loader:1117:32)at Module._load (node:internal/modules/cjs/loader:958:12)at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)at node:internal/main/run_main_module:23:47 {code: 'ERR_UNHANDLED_ERROR',context: undefined}2*/
为了避免程序崩溃,我们可以使用 try-catch 包裹一下 eIns.emit('error')。这样可以保证即使出现错误,程序也能够继续执行,并采取适当的措施来处理错误情况。
const {EventEmitter} = require('events')const eIns = new EventEmitter()console.log(1)eIns.on('error', (...args) => {console.error('[emit error event]:', ...args)})eIns.emit('error', 1, 2, 3)console.log(2)/*1[emit error event]: 1 2 32*/
如果不想使用 try-catch,同时又要避免程序崩溃,建议在使用 EventEmitter 的时候,始终为 error 事件注册一个监听器,以便能够捕获和处理错误。
const fs = require('fs');const {EventEmitter} = require('events')const readStream = fs.createReadStream('1.txt');console.log('[readStream instanceof EventEmitter]:', readStream instanceof EventEmitter)readStream.on('error', (error) => {console.error('发生错误:', error);});readStream.on('data', (data) => {console.log('文件内容:', data.toString());});/*[readStream instanceof EventEmitter]: true文件内容: 现在时间:23.05.18 周四 下午 11:22写完这个 demo 滚去睡觉了*/
现在时间:23.05.18 周四 下午 11:22写完这个 demo 滚去睡觉了
