zl程序教程

您现在的位置是:首页 >  IT要闻

当前栏目

面试官:说说Node中的EventEmitter? 如何实现一个EventEmitter?

2023-03-09 22:07:15 时间

本文转载自微信公众号「JS每日一题」,作者灰灰。转载本文请联系JS每日一题公众号。

一、是什么

我们了解到,Node采用了事件驱动机制,而EventEmitter就是Node实现事件驱动的基础

在EventEmitter的基础上,Node几乎所有的模块都继承了这个类,这些模块拥有了自己的事件,可以绑定/触发监听器,实现了异步操作

Node.js 里面的许多对象都会分发事件,比如 fs.readStream 对象会在文件被打开的时候触发一个事件

这些产生事件的对象都是 events.EventEmitter 的实例,这些对象有一个 eventEmitter.on() 函数,用于将一个或多个函数绑定到命名事件上

二、使用方法

Node的events模块只提供了一个EventEmitter类,这个类实现了Node异步事件驱动架构的基本模式——观察者模式

在这种模式中,被观察者(主体)维护着一组其他对象派来(注册)的观察者,有新的对象对主体感兴趣就注册观察者,不感兴趣就取消订阅,主体有更新的话就依次通知观察者们

基本代码如下所示:

  1. const EventEmitter = require('events'
  2.  
  3. class MyEmitter extends EventEmitter {} 
  4. const myEmitter = new MyEmitter() 
  5.  
  6. function callback() { 
  7.     console.log('触发了event事件!'
  8. myEmitter.on('event', callback) 
  9. myEmitter.emit('event'
  10. myEmitter.removeListener('event', callback); 

通过实例对象的on方法注册一个名为event的事件,通过emit方法触发该事件,而removeListener用于取消事件的监听

关于其常见的方法如下:

  • 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 的监听事件

三、实现过程

通过上面的方法了解,EventEmitter是一个构造函数,内部存在一个包含所有事件的对象

  1. class EventEmitter { 
  2.     constructor() { 
  3.         this.events = {}; 
  4.     } 

其中events存放的监听事件的函数的结构如下:

  1.   "event1": [f1,f2,f3], 
  2.   "event2": [f4,f5], 
  3.   ... 

然后开始一步步实现实例方法,首先是emit,第一个参数为事件的类型,第二个参数开始为触发事件函数的参数,实现如下:

  1. emit(type, ...args) { 
  2.     this.events[type].forEach((item) => { 
  3.         Reflect.apply(item, this, args); 
  4.     }); 

当实现了emit方法之后,然后实现on、addListener、prependListener这三个实例方法,都是添加事件监听触发函数,实现也是大同小异

  1. on(type, handler) { 
  2.     if (!this.events[type]) { 
  3.         this.events[type] = []; 
  4.     } 
  5.     this.events[type].push(handler); 
  6.  
  7. addListener(type,handler){ 
  8.     this.on(type,handler) 
  9.  
  10. prependListener(type, handler) { 
  11.     if (!this.events[type]) { 
  12.         this.events[type] = []; 
  13.     } 
  14.     this.events[type].unshift(handler); 

紧接着就是实现事件监听的方法removeListener/on

  1. removeListener(type, handler) { 
  2.     if (!this.events[type]) { 
  3.         return
  4.     } 
  5.     this.events[type] = this.events[type].filter(item => item !== handler); 
  6.  
  7. off(type,handler){ 
  8.     this.removeListener(type,handler) 

最后再来实现once方法, 再传入事件监听处理函数的时候进行封装,利用闭包的特性维护当前状态,通过fired属性值判断事件函数是否执行过

  1. once(type, handler) { 
  2.     this.on(type, this._onceWrap(type, handler, this)); 
  3.   } 
  4.  
  5.   _onceWrap(type, handler, target) { 
  6.     const state = { fired: false, handler, type , target}; 
  7.     const wrapFn = this._onceWrapper.bind(state); 
  8.     state.wrapFn = wrapFn; 
  9.     return wrapFn; 
  10.   } 
  11.  
  12.   _onceWrapper(...args) { 
  13.     if (!this.fired) { 
  14.       this.fired = true
  15.       Reflect.apply(this.handler, this.target, args); 
  16.       this.target.off(this.type, this.wrapFn); 
  17.     } 
  18.  } 

完整代码如下:

  1. class EventEmitter { 
  2.     constructor() { 
  3.         this.events = {}; 
  4.     } 
  5.  
  6.     on(type, handler) { 
  7.         if (!this.events[type]) { 
  8.             this.events[type] = []; 
  9.         } 
  10.         this.events[type].push(handler); 
  11.     } 
  12.  
  13.     addListener(type,handler){ 
  14.         this.on(type,handler) 
  15.     } 
  16.  
  17.     prependListener(type, handler) { 
  18.         if (!this.events[type]) { 
  19.             this.events[type] = []; 
  20.         } 
  21.         this.events[type].unshift(handler); 
  22.     } 
  23.  
  24.     removeListener(type, handler) { 
  25.         if (!this.events[type]) { 
  26.             return
  27.         } 
  28.         this.events[type] = this.events[type].filter(item => item !== handler); 
  29.     } 
  30.  
  31.     off(type,handler){ 
  32.         this.removeListener(type,handler) 
  33.     } 
  34.  
  35.     emit(type, ...args) { 
  36.         this.events[type].forEach((item) => { 
  37.             Reflect.apply(item, this, args); 
  38.         }); 
  39.     } 
  40.  
  41.     once(type, handler) { 
  42.         this.on(type, this._onceWrap(type, handler, this)); 
  43.     } 
  44.  
  45.     _onceWrap(type, handler, target) { 
  46.         const state = { fired: false, handler, type , target}; 
  47.         const wrapFn = this._onceWrapper.bind(state); 
  48.         state.wrapFn = wrapFn; 
  49.         return wrapFn; 
  50.     } 
  51.  
  52.     _onceWrapper(...args) { 
  53.         if (!this.fired) { 
  54.             this.fired = true
  55.             Reflect.apply(this.handler, this.target, args); 
  56.             this.target.off(this.type, this.wrapFn); 
  57.         } 
  58.     } 

测试代码如下:

  1. const ee = new EventEmitter(); 
  2.  
  3. // 注册所有事件 
  4. ee.once('wakeUp', (name) => { console.log(`${name} 1`); }); 
  5. ee.on('eat', (name) => { console.log(`${name} 2`) }); 
  6. ee.on('eat', (name) => { console.log(`${name} 3`) }); 
  7. const meetingFn = (name) => { console.log(`${name} 4`) }; 
  8. ee.on('work', meetingFn); 
  9. ee.on('work', (name) => { console.log(`${name} 5`) }); 
  10.  
  11. ee.emit('wakeUp''xx'); 
  12. ee.emit('wakeUp''xx');         // 第二次没有触发 
  13. ee.emit('eat''xx'); 
  14. ee.emit('work''xx'); 
  15. ee.off('work', meetingFn);        // 移除事件 
  16. ee.emit('work''xx');           // 再次工作 

参考文献

 

  • http://nodejs.cn/api/events.html#events_class_eventemitter
  • https://segmentfault.com/a/1190000015762318
  • https://juejin.cn/post/6844903781230968845
  • https://vue3js.cn/interview