Nodejs 的大部分核心 API 都是基于异步事件驱动设计的,事件驱动核心是通过 node 中 Events 对象来实现事件的发送和监听回调绑定,我们常用的 stream 模块也是依赖于 Events 模块是来实现数据流之间的回调通知,如在数据到来时触发 data 事件,流对象为可读状态触发 readable 事件,当数据读写完毕后发送 end 事件。
既然 Events 模块如此重要,我们有必要来学习一下 Events 模块的基本使用,以及如何模拟实现 Events 模块中常用的 api
一、Events 模块的基本使用以及简单实现
首先我们了解一下 Events 模块的基本用法,其实 Events 模块本质上是观察者模式的实现,所谓观察者模式就是:
它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知
观察者模式有对应的观察者以及被观察的对象,在 Events 模块中,对应的实现就是 on 和 emit 函数
const EventEmitter = require('events');class MyEmitter extends EventEmitter {}const myEmitter = new MyEmitter();myEmitter.on('嗨', (str) => { console.log(str);});myEmitter.emit('嗨','你好');
从上述的使用中,我们可以知道 on 是用来监听事件的发生,而 emit 是用来触发事件的发生,一旦 emit 触发了事件,on 就会被通知到,从而执行对应的回调函数。
有了这个实例,我们可以思考下如何实现这个 EventEmitter 类。
思路:当我们执行 on 函数时,我们可以将回调函数保存起来,等到 emit 触发了事件时,将回调函数拿出来执行,那么就可以实现了事件的监听以及订阅了。
class EventEmitter{ constructor(){ #事件监听函数保存的地方 this.events={}; } on(eventName,listener){ if (this.events[eventName]) { this.events[eventName].push(listener); } else { #如果没有保存过,将回调函数保存为数组 this.events[eventName] = [listener]; } } emit(eventName){ #emit触发事件,把回调函数拉出来执行 this.events[eventName] && this.events[eventName].forEach(listener => listener()) }}
上述就实现了一个简单的 EventEmitter 类,下面来实例一下:
let event = new EventEmitter();event.on('嗨',function(){ console.log('你好');});event.emit('嗨');#输出:你好
完善:我们注意到在原生的 EventEmitter 类中,emit 是可以传递参数到我们的回调函数中,那么我们实现的类也应该支持传递参数。我们对 emit 进行如下更改
emit(eventName,...rest){ #emit触发事件,把回调函数拉出来执行 this.events[eventName] && this.events[eventName].forEach(listener => listener.apply(this,rest))}
新闻热点
疑难解答
图片精选