# events-观察者模式
# 前言
events模块是node的核心模块,几乎所有常用的node模块都继承了events模块,比如http、fs等。本文将通过实现一版简单的 EventEmitter
来介绍nodeJS中的事件机制
# EventEmitter
多数 Node.js 核心 API 都是采用惯用的异步事件驱动架构,其中某些类型的对象(称为触发器)会周期性地触发命名事件来调用函数对象(监听器)。例如,一个net.Server对象会在每次有新连接时触发一个事件;一个 fs.ReadStream 会在文件被打开时触发一个事件;一个 stream会在数据可读时触发事件等。
# EventEmitter的简单使用
const EventEmitter =require('events');
const util = require('util')
function Person(){
}
util.inherits(Person, EventEmitter); // Person 继承 EventEmitter类 拥有 on、emit、off、once、newListener 等方法
const person = new Person();
let listener1 = ()=>{
console.log('吃完前洗手')
}
person.on('eat', listener1);
let listener2 = ()=>{
console.log('吃完前洗手')
}
person.on('eat', listener2);
person.emit('eat');
从代码可以看出, Person
继承 EventEmitter
类,可以拥有EventEmitter
上的方法,通过 on
方法挂载两个函数 listener1
listener2
,最后通过 emit
调用挂载到 eat
上的所有方法,实现一个简单的发布订阅。
其实内部的结构很简单,通过 on
方法挂载两个方法的结构如下:
_events = {
'eat': [listener1, listener2]
}
而 emit
方法只是根据 eat
找到对应的数组遍历执行。
# 实现 on
和 emit
在上面我们已经知道了 on
和 emit
的基本原理,那我们试一下自己来简单实现一下.
function EventEmitter() {
this._enents = Object.create(null) // _enents存放当前全部订阅
}
EventEmitter.prototype.on = function(eventName, fn) {
let arr = this._enents[eventName] || (this._enents[eventName] = [])
arr.push(fn)
}
EventEmitter.prototype.emit = function(eventName) {
if (this._enents[eventName]) {
this._enents[eventName].forEach(fn => {
fn()
});
}
}
module.exports = EventEmitter
但上面代码执行的时候会发现报了一个错
let arr = this._enents[eventName] || (this._enents[eventName] = [])
^
TypeError: Cannot read property 'eat' of undefined
经过排查才发现 我们再调用on
方法的时候,this指向person实例,但是person实例上面没有 this._enents
,所以报错
那我们再优化下代码,做个边界处理吧
function EventEmitter() {
this._events = Object.create(null)
}
EventEmitter.prototype.on = function(eventName, fn) {
if (!this._events) {
this._events = Object.create(null)
}
let arr = this._events[eventName] || (this._events[eventName] = [])
arr.push(fn)
}
EventEmitter.prototype.emit = function(eventName) {
if (this._events[eventName]) {
this._events[eventName].forEach(fn => {
fn()
});
}
}
那我们目前就实现了一个最简单的发布订阅了.
# 实现 off
、newListener
、once
EventEmitter
上还有其他常见的用法 如 off
、newListener
和 once
, 那我们先来看下他们的用法吧
off: 删除挂载的函数
const EventEmitter =require('events');
const util = require('util')
function Person(){
}
util.inherits(Person, EventEmitter);
const person = new Person();
let listener2 = ()=>{
console.log('吃完前洗手')
}
person.on('eat', listener2);
person.off('eat',listener2);
person.emit('eat');
从代码我们可以发现,通过 on
挂载了一个 eat
, 但是后面用 off
对他进行删除,所以 emit
发布的时候没有打印出任何东西.
newListener: 可以获取到代码中后序调用 on
或者 once
方法中传入的 `
const EventEmitter =require('events');
const util = require('util')
function Person(){
}
util.inherits(Person, EventEmitter);
const person = new Person();
person.on('newListener', (type)=>{
process.nextTick(()=>{
if(type === 'eat'){
person.emit('eat');
}
})
})
let listener2 = ()=>{
console.log('吃完前洗手')
}
person.on('eat', listener2);
通过代码可以发现, on
方法如果传入 newListener的话,其回调函数可以接受一个参数 type, type是代码后序使用 on
或者 once
挂载的 eventName
,然后来进行逻辑处理
once: 和 on
一样是挂载,但是挂载的函数执行之后就会清除
const EventEmitter =require('events');
const util = require('util')
function Person(){
}
util.inherits(Person, EventEmitter);
const person = new Person();
let listener2 = ()=>{
console.log('吃完前洗手')
}
person.once('eat', listener2);
person.emit('eat')
person.emit('eat')
代码的运行结果是只打印一次 '吃完前洗手',表示 once
方法挂载的方法只能被执行一次
那知道大致的用法之后,手也忍不住的自己实现一版了
# off 的实现
EventEmitter.prototype.off = function(eventName,fn){
if( this._events[eventName]){
this._events[eventName] = this._events[eventName].filter(item=>{
return item !== fn && item.l !== fn
})
}
}
# newListener 的实现
EventEmitter.prototype.on = function(eventName,callback){
if(!this._events){
this._events = Object.create(null);
}
if(eventName !== 'newListener'){
if(this._events['newListener']){
this._events['newListener'].forEach(fn=>fn(eventName))
}
}
let arr = this._events[eventName] || (this._events[eventName] = []);
arr.push(callback)
}
# once 的实现
EventEmitter.prototype.once = function(eventName,callback){
const once = (...args)=>{ // 高阶函数
callback(...args);// 先执行原有的逻辑
this.off(eventName,once);// 在将这个函数移除掉
}
once.l = callback
this.on(eventName,once);
}
# 源码
function EventEmitter(){
this._events = Object.create(null);
}
// 订阅 创建关系
EventEmitter.prototype.on = function(eventName,callback){
if(!this._events){
this._events = Object.create(null);
}
if(eventName !== 'newListener'){
if(this._events['newListener']){
this._events['newListener'].forEach(fn=>fn(eventName))
}
}
let arr = this._events[eventName] || (this._events[eventName] = []);
arr.push(callback)
}
EventEmitter.prototype.once = function(eventName,callback){
const once = (...args)=>{ // 高阶函数
callback(...args);// 先执行原有的逻辑
this.off(eventName,once);// 在将这个函数移除掉
}
once.l = callback
this.on(eventName,once);
}
// 让对应的函数依次执行
EventEmitter.prototype.emit = function(eventName,...args){
if(!this._events){
this._events = Object.create(null);
}
if( this._events[eventName]){
this._events[eventName].forEach(fn => fn(...args));
}
}
EventEmitter.prototype.off = function(eventName,fn){
if( this._events[eventName]){
// this._events[eventName] = [once]
this._events[eventName] = this._events[eventName].filter(item=>{
return item !== fn && item.l !== fn
})
}
}
module.exports = EventEmitter;
← 你不知道的require 说说缓存的那些事 →