目标:通过 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 event
emit event
emit 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")
/*
1
2
3
4
*/
emit 一个不存在的事件,相当于什么也没做
const {
EventEmitter
} = require('events')
const eIns = new EventEmitter()
console.log(1)
eIns.emit('event')
console.log(2)
/*
1
2
*/
当我们在 EventEmitter 实例上 emit 一个不存在的事件时,它将静默失败,没有任何影响,不会引发错误或抛出异常。
emit(‘error’)
const {
EventEmitter
} = require('events')
const eIns = new EventEmitter()
console.log(1)
// eIns.on('error', () => {
// // ...
// })
eIns.emit('error')
console.log(2)
/*
1
node:events:504
throw 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 3
2
*/
如果不想使用 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 滚去睡觉了