目标:通过 demos 对事件驱动架构有个初步的认识

简述:
EventEmitter 在实际工作中使用相对较少,快速将 demos 给过一遍即可。重点在于认识“事件驱动架构”是一种什么样的编程体验,这对于我们学习那些基于 NodeJS 实现的框架而言,帮助是很大的。比如,Electron 就是基于 NodeJS 来实现的,在 Electron 中,用于实现 IPC 通信的模块 ipcMain、ipcRenderer 都是基于 EventEmitter 实现的,了解 EventEmitter 的一些常用 API,有助于我们快速上手 ipcMain、ipcRenderer。

参考资料:

概述

EventEmitter 是 Node.js 的核心模块之一,它提供了实现 事件驱动架构 的基本工具。在 Node.js 和基于 Node.js 的应用程序(例如 Electron)中,事件驱动架构是一个核心的设计理念。

  1. 异步编程:在 Node.js 中,异步编程是常态,EventEmitter 可以让你在某件事情完成(或者发生错误)时触发回调函数。
  2. 模块间解耦:通过事件,不同的模块可以相互通信,而不需要直接引用对方。
  3. 实现观察者模式:EventEmitter 提供了一种实现观察者模式的机制,观察者模式是一种设计模式,它定义了对象间的一种一对多的关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。
  4. 提升应用程序性能:通过使用事件驱动的编程模型,我们可以非阻塞地处理 I/O,这可以让我们的应用程序在等待 I/O(如网络请求或文件读取)完成时做其他事情,从而提高应用程序的性能。

对于 Node.js 和基于 Node.js 的开发者来说,理解和掌握 EventEmitter 是非常重要的。

on、emit、addListener

  1. const {
  2. EventEmitter
  3. } = require('events')
  4. const eIns = new EventEmitter()
  5. eIns.on('event', () => {
  6. console.log('emit event')
  7. })
  8. // eIns.addListener('event', () => {
  9. // console.log('emit event')
  10. // })
  11. eIns.emit('event')
  12. /*
  13. emit event
  14. */

addListener 和 on 是 EventEmitter 类的两个方法,它们在功能上是完全相同的,用于注册事件监听器。实际上,on 方法就是 addListener 方法的别名,它们可以互换使用,没有实质性的区别。我们可以根据个人喜好和代码风格选择使用 addListener 或 on

多次 emit

  1. const {
  2. EventEmitter
  3. } = require('events')
  4. const eIns = new EventEmitter()
  5. eIns.on('event', () => {
  6. console.log('emit event')
  7. })
  8. eIns.emit('event')
  9. eIns.emit('event')
  10. eIns.emit('event')
  11. /*
  12. emit event
  13. emit event
  14. emit event
  15. */

传递参数

  1. const {
  2. EventEmitter
  3. } = require('events')
  4. const eIns = new EventEmitter()
  5. eIns.on('event', (...args) => {
  6. console.log('[emit event]:', ...args)
  7. })
  8. eIns.emit('event', 1)
  9. eIns.emit('event', 1, 2)
  10. eIns.emit('event', 1, 2, 3)
  11. /*
  12. [emit event]: 1
  13. [emit event]: 1 2
  14. [emit event]: 1 2 3
  15. */

once

  1. const {
  2. EventEmitter
  3. } = require('events')
  4. const eIns = new EventEmitter()
  5. eIns.once('event', (...args) => {
  6. console.log('[emit event]:', ...args)
  7. })
  8. eIns.emit('event', 1)
  9. eIns.emit('event', 1, 2)
  10. eIns.emit('event', 1, 2, 3)
  11. /*
  12. [emit event]: 1
  13. */

off、removeListener

  1. const {
  2. EventEmitter
  3. } = require('events')
  4. const eIns = new EventEmitter()
  5. function cb(...args) {
  6. console.log('[emit event]:', ...args)
  7. }
  8. eIns.on('event', cb)
  9. eIns.emit('event', 1)
  10. eIns.emit('event', 1, 2)
  11. eIns.off('event', cb)
  12. // eIns.removeListener('event', cb)
  13. eIns.emit('event', 1, 2, 3)
  14. /*
  15. [emit event]: 1
  16. [emit event]: 1 2
  17. */

与 on、addListener 一样,off 和 removeListener 也是等效的,根据自己习惯,选择一个喜欢的使用即可。

注意:

  • 如果我们在绑定 event 事件的监听器,使用的是匿名函数式的写法,那么 removeListener 是没法用的。因为匿名函数没有函数名,这将导致我们没法找到这个函数引用(监听器)。

多次 on

  1. const {
  2. EventEmitter
  3. } = require('events')
  4. const eIns = new EventEmitter()
  5. function cb(...args) {
  6. console.log('[emit event]:', ...args)
  7. }
  8. eIns.on('event', cb)
  9. eIns.on('event', cb)
  10. eIns.on('event', cb)
  11. eIns.emit('event', 1)
  12. eIns.off('event', cb)
  13. eIns.emit('event', 1, 2)
  14. eIns.off('event', cb)
  15. eIns.emit('event', 1, 2, 3)
  16. /*
  17. [emit event]: 1
  18. [emit event]: 1
  19. [emit event]: 1
  20. [emit event]: 1 2
  21. [emit event]: 1 2
  22. [emit event]: 1 2 3
  23. */

listenerCount

  1. const {
  2. EventEmitter
  3. } = require('events')
  4. const eIns = new EventEmitter()
  5. function cb(...args) {
  6. console.log('[emit event]:', ...args)
  7. }
  8. eIns.on('event', cb)
  9. eIns.on('event', cb)
  10. eIns.on('event', cb)
  11. console.log(`[eIns.listenerCount('event')]:`, eIns.listenerCount('event'))
  12. eIns.emit('event', 1)
  13. eIns.removeListener('event', cb)
  14. console.log(`[eIns.listenerCount('event')]:`, eIns.listenerCount('event'))
  15. eIns.emit('event', 1, 2)
  16. eIns.removeListener('event', cb)
  17. console.log(`[eIns.listenerCount('event')]:`, eIns.listenerCount('event'))
  18. eIns.emit('event', 1, 2, 3)
  19. /*
  20. [eIns.listenerCount('event')]: 3
  21. [emit event]: 1
  22. [emit event]: 1
  23. [emit event]: 1
  24. [eIns.listenerCount('event')]: 2
  25. [emit event]: 1 2
  26. [emit event]: 1 2
  27. [eIns.listenerCount('event')]: 1
  28. [emit event]: 1 2 3
  29. */

removeAllListeners

  1. const {
  2. EventEmitter
  3. } = require('events')
  4. const eIns = new EventEmitter()
  5. function cb(...args) {
  6. console.log('[emit event]:', ...args)
  7. }
  8. eIns.on('event', cb)
  9. eIns.on('event', cb)
  10. eIns.on('event', cb)
  11. console.log(`[eIns.listenerCount('event')]:`, eIns.listenerCount('event'))
  12. eIns.emit('event', 1)
  13. eIns.removeListener('event', cb)
  14. console.log(`[eIns.listenerCount('event')]:`, eIns.listenerCount('event'))
  15. eIns.emit('event', 1, 2)
  16. eIns.removeAllListeners('event')
  17. console.log(`[eIns.listenerCount('event')]:`, eIns.listenerCount('event'))
  18. eIns.emit('event', 1, 2, 3)
  19. /*
  20. [eIns.listenerCount('event')]: 3
  21. [emit event]: 1
  22. [emit event]: 1
  23. [emit event]: 1
  24. [eIns.listenerCount('event')]: 2
  25. [emit event]: 1 2
  26. [emit event]: 1 2
  27. [eIns.listenerCount('event')]: 0
  28. */
  1. const {
  2. EventEmitter
  3. } = require('events')
  4. const eIns = new EventEmitter()
  5. eIns.on('event', (...args) => {
  6. console.log('[emit event]:', ...args)
  7. })
  8. eIns.on('event', (...args) => {
  9. console.log('[emit event]:', ...args)
  10. })
  11. eIns.emit('event', 1)
  12. eIns.emit('event', 1, 2)
  13. eIns.removeAllListeners('event')
  14. eIns.emit('event', 1, 2, 3)
  15. /*
  16. [emit event]: 1
  17. [emit event]: 1
  18. [emit event]: 1 2
  19. [emit event]: 1 2
  20. */

立即执行

  1. const {
  2. EventEmitter
  3. } = require('events')
  4. const eIns = new EventEmitter()
  5. eIns.on('event1', () => {
  6. console.log('1')
  7. eIns.emit('event2')
  8. console.log('3')
  9. })
  10. eIns.on('event2', () => {
  11. console.log('2')
  12. })
  13. eIns.emit('event1')
  14. console.log("4")
  15. /*
  16. 1
  17. 2
  18. 3
  19. 4
  20. */

emit 一个不存在的事件,相当于什么也没做

  1. const {
  2. EventEmitter
  3. } = require('events')
  4. const eIns = new EventEmitter()
  5. console.log(1)
  6. eIns.emit('event')
  7. console.log(2)
  8. /*
  9. 1
  10. 2
  11. */

当我们在 EventEmitter 实例上 emit 一个不存在的事件时,它将静默失败,没有任何影响,不会引发错误或抛出异常。

emit(‘error’)

  1. const {
  2. EventEmitter
  3. } = require('events')
  4. const eIns = new EventEmitter()
  5. console.log(1)
  6. // eIns.on('error', () => {
  7. // // ...
  8. // })
  9. eIns.emit('error')
  10. console.log(2)
  11. /*
  12. 1
  13. node:events:504
  14. throw err; // Unhandled 'error' event
  15. ^
  16. Error [ERR_UNHANDLED_ERROR]: Unhandled error. (undefined)
  17. at new NodeError (node:internal/errors:399:5)
  18. at EventEmitter.emit (node:events:502:17)
  19. at Object.<anonymous> (/Users/huyouda/0000/0.js:9:6)
  20. at Module._compile (node:internal/modules/cjs/loader:1254:14)
  21. at Module._extensions..js (node:internal/modules/cjs/loader:1308:10)
  22. at Module.load (node:internal/modules/cjs/loader:1117:32)
  23. at Module._load (node:internal/modules/cjs/loader:958:12)
  24. at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
  25. at node:internal/main/run_main_module:23:47 {
  26. code: 'ERR_UNHANDLED_ERROR',
  27. context: undefined
  28. }
  29. Node.js v18.15.0
  30. */

所有的 EventEmitter 实例默认都会注册一个 error 事件监听器。这意味着如果在 EventEmitter 实例中没有显式地注册 error 事件监听器,当触发了一个错误事件且没有对其进行处理时,Node.js 将会打印错误堆栈信息,并可能导致程序退出。

  1. const {
  2. EventEmitter
  3. } = require('events')
  4. const eIns = new EventEmitter()
  5. console.log(1)
  6. try {
  7. eIns.emit('error')
  8. } catch (error) {
  9. console.error('[emit error event]:', error)
  10. }
  11. console.log(2)
  12. /*
  13. 1
  14. [emit error event]: Error [ERR_UNHANDLED_ERROR]: Unhandled error. (undefined)
  15. at new NodeError (node:internal/errors:399:5)
  16. at EventEmitter.emit (node:events:502:17)
  17. at Object.<anonymous> (/Users/huyouda/0000/0.js:10:8)
  18. at Module._compile (node:internal/modules/cjs/loader:1254:14)
  19. at Module._extensions..js (node:internal/modules/cjs/loader:1308:10)
  20. at Module.load (node:internal/modules/cjs/loader:1117:32)
  21. at Module._load (node:internal/modules/cjs/loader:958:12)
  22. at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
  23. at node:internal/main/run_main_module:23:47 {
  24. code: 'ERR_UNHANDLED_ERROR',
  25. context: undefined
  26. }
  27. 2
  28. */

为了避免程序崩溃,我们可以使用 try-catch 包裹一下 eIns.emit('error')。这样可以保证即使出现错误,程序也能够继续执行,并采取适当的措施来处理错误情况。

  1. const {
  2. EventEmitter
  3. } = require('events')
  4. const eIns = new EventEmitter()
  5. console.log(1)
  6. eIns.on('error', (...args) => {
  7. console.error('[emit error event]:', ...args)
  8. })
  9. eIns.emit('error', 1, 2, 3)
  10. console.log(2)
  11. /*
  12. 1
  13. [emit error event]: 1 2 3
  14. 2
  15. */

如果不想使用 try-catch,同时又要避免程序崩溃,建议在使用 EventEmitter 的时候,始终为 error 事件注册一个监听器,以便能够捕获和处理错误。

  1. const fs = require('fs');
  2. const {
  3. EventEmitter
  4. } = require('events')
  5. const readStream = fs.createReadStream('1.txt');
  6. console.log('[readStream instanceof EventEmitter]:', readStream instanceof EventEmitter)
  7. readStream.on('error', (error) => {
  8. console.error('发生错误:', error);
  9. });
  10. readStream.on('data', (data) => {
  11. console.log('文件内容:', data.toString());
  12. });
  13. /*
  14. [readStream instanceof EventEmitter]: true
  15. 文件内容: 现在时间:
  16. 23.05.18 周四 下午 11:22
  17. 写完这个 demo 滚去睡觉了
  18. */
  1. 现在时间:
  2. 23.05.18 周四 下午 11:22
  3. 写完这个 demo 滚去睡觉了