深入解析 Node.js Events 模块:原理、使用

60 阅读4分钟

menu:

  1. events 模块作用是什么?
  2. events 使用方法
  3. 实现 EventEmitter 模块

events 模块作用

events 模块是 NodeJS 中内置的 发布与订阅 模块 events 有诸多使用场景 包括但不限于以下场景:

  1. 比如最常用的 Promise 内部实现就是 发布与订阅
  2. 日志功能(日志监听与触发事件进行记录)
  3. 流处理(监听流事件)
  4. 网络通信(监听与数据传输)

events 使用方式

events 模块中我们主要使用以下方法: on 注册并绑定事件处理函数 prependListener 注册并绑定事件处理函数(放在队头) once 注册并绑定事件处理函数 只执行一次就删除 prependOnceListener 注册并绑定事件处理函数(放在队头) 只执行一次就删除 off 解除绑定 emits 触发事件 rawListener 返回一个 事件处理函数队列 eventNames 返回所有绑定的事件名称 removeAllListeners 删除 多个或全部 事件 以及处理函数 listenerCount 计算对应事件中处理函数的个数

on 注册并绑定事件处理函数

const girl = new Girl();

const ACTION = {
  EAT: 'eat',
  LEARNING: 'learning',
  RUNNING: 'running'
};

// 订阅
girl.on(ACTION.EAT, (food) => {
  console.log(food);
});

girl.on(ACTION.EAT, (food) => {
  console.log(food);
});

// 发布
girl.emit(ACTION.EAT, 'fish');
girl.emit(ACTION.EAT, 'fish');

prependListener 注册并绑定事件处理函数(放在队头)

const girl = new Girl();

const ACTION = {
  EAT: 'eat',
  LEARNING: 'learning',
  RUNNING: 'running'
};

// 订阅
girl.prependListener(ACTION.EAT, (food) => {
  console.log(food);
});

girl.prependListener(ACTION.LEARNING, (book) => {
  console.log(book);
});


// 发布

girl.emit(ACTION.LEARNING, 'Math,English');

girl.emit(ACTION.EAT, 'fish');

prependOnceListener 注册并绑定事件处理函数(放在队头) 只执行一次就删除

const girl = new Girl();
  
const ACTION = {
  EAT: 'eat',
  LEARNING: 'learning',
  RUNNING: 'running'
};

// 订阅
girl.prependOnceListener(ACTION.EAT, (food) => {
  console.log(food);
});

girl.prependOnceListener(ACTION.LEARNING, (book) => {
  console.log(book);
});

// 发布
girl.emit(ACTION.LEARNING, 'Math,English');
girl.emit(ACTION.EAT, 'fish');
girl.emit(ACTION.LEARNING, 'Math,English');
girl.emit(ACTION.EAT, 'fish');

rawListeners 返回一个 事件处理函数队列

const girl = new Girl();

const ACTION = {
  EAT: 'eat',
  LEARNING: 'learning',
  RUNNING: 'running'
};

// 订阅
girl.on(ACTION.EAT, (food) => {
  console.log(food);
});

girl.on(ACTION.LEARNING, (book) => {
  console.log(book);
});

// 发布
girl.emit(ACTION.LEARNING, 'Math,English');
girl.emit(ACTION.EAT, 'fish');
console.log(girl.rawListeners(ACTION.LEARNING)); // [ [Function (anonymous)] ]
console.log(girl.rawListeners(ACTION.EAT)); // [ [Function (anonymous)] ]

eventNames 返回所有 事件队列名称

const girl = new Girl();

const ACTION = {
  EAT: 'eat',
  LEARNING: 'learning',
  RUNNING: 'running'
};

// 订阅
girl.on(ACTION.EAT, (food) => {
  console.log(food);
});

girl.on(ACTION.LEARNING, (book) => {
  console.log(book);
});

// 发布
girl.emit(ACTION.LEARNING, 'Math,English');
girl.emit(ACTION.EAT, 'fish');
console.log(girl.eventNames()); // [ 'eat', 'learning' ]

removeAllListeners 删除多个或所有

const girl = new Girl();

const ACTION = {
  EAT: 'eat',
  LEARNING: 'learning',
  RUNNING: 'running'
};

// 订阅
girl.on(ACTION.EAT, (food) => {
  console.log(food);
});

girl.on(ACTION.LEARNING, (book) => {
  console.log(book);
});


// 发布
girl.emit(ACTION.LEARNING, 'Math,English');
girl.emit(ACTION.EAT, 'fish');

girl.removeAllListeners([ACTION.LEARNING]);

console.log(girl.eventNames()); // [ 'eat' ]

girl.on(ACTION.EAT, (food) => {
  console.log(food);
});

girl.on(ACTION.LEARNING, (book) => {
  console.log(book);
});

girl.removeAllListeners(); // 清空
console.log(girl.eventNames()); // []

listenerCount 获取事件队列的 事件处理函数对象

const girl = new Girl();

const ACTION = {
  EAT: 'eat',
  LEARNING: 'learning',
  RUNNING: 'running'
};

// 订阅
girl.on(ACTION.EAT, (food) => {
  console.log(food);
});

girl.on(ACTION.LEARNING, (book) => {
  console.log(book);
});

girl.on(ACTION.LEARNING, (book) => {
  console.log(book);
});
  
console.log(girl.listenerCount(ACTION.LEARNING)); // 2
console.log(girl.listenerCount(ACTION.EAT)); // 1

EventEmitter 实现

const EVENT_NAMES = {
  NEWLISTENER: 'newListener',
  REMOVELISTENER: 'removeListener'
};

function EventEmitter(opts) {
  EventEmitter.init.call(this, opts);
}

// 初始化
EventEmitter.init = function (opts) {
  if (isVoid(this._events)) {
    // 事件映射表
    this._events = Object.create(null);
  }
}

// 将事件映射 事件处理函数
function _addListener(type, listener, prepend) {
  // 检查是否是 函数
  checkListener(listener);

  let events = this._events,
    existsEvent = null;

  // 不存在则初始化
  if (isVoid(events)) {
    this._events = Object.create(null);
    events = this._events;
  } else {
    // 当存在 newListener 事件时 添加事件则需要触发 newListener
    if (!isVoid(events.newListener)) {
      this.emit(EVENT_NAMES.NEWLISTENER, type, listener.listener ?? listener);
      events = this._events;
    }
    
    // 是否存在该事件
    existsEvent = events[type];
  }

  // 不存在该事件
  if (isVoid(existsEvent)) {
    events[type] = [];
  }

  // 添加事件处理函数
  if (prepend) {
    events[type].unshift(listener);
  } else {
    events[type].push(listener);
  }
  
  this._events[type] = events[type];
}

// 事件移除
function _removeListener(type, listener) {
  checkListener(listener);

  let events = this._events;
  if (isVoid(events)) return this;
  
  // 获取 事件处理函数列表
  let listeners = events[type];
  
  if (isVoid(listeners)) return this;
  
  listeners = listeners.filter(listenerCallback => listener !== listenerCallback);
  
  this._events[type] = listeners;

  events = this._events;

  if (listeners.length === 0) {
    delete this._events[type];
  }

  if (!isVoid(events.removeListener)) {
    this.emit(EVENT_NAMES.REMOVELISTENER, type, listener);
  }
}

// 一件删除多个或全部
function _removeListeners(...types) {

  let events = this._events;

  if (isVoid(events)) return this;

  
  // 处理 不存在 removeListener 事件的情况
  if (isVoid(events.removeListener)) {
    if (types.length === 0) {
      // 删除全部
      this._events = Object.create(null);
    } else {
      for (const type of types) {
        const existsKey = events[type];
        if (existsKey) {
          delete events[type];
          removeListener.call(this, type);
        }
      }
      const isEvents = Reflect.ownKeys(events).length > 1;
      !isEvents && removeListener.call(this, EVENT_NAMES.REMOVELISTENER);
    }
    return this;
  }
  

  if (types.length === 0) {
    this._events = Object.create(null);
    events = this._events;
    return this;
  }

  if (types.length > 0) {
    for (const type of types) {
      const existsKey = events[type];
      if (existsKey) {
        delete events[type];
        removeListener.call(this, type);
      }
    }
    const isEvents = Reflect.ownKeys(events).length > 1;
    !isEvents && removeListener.call(this, EVENT_NAMES.REMOVELISTENER);
    return this;
  }

  function removeListener(key) {
    return delete this._events[key];
  }
}


// 获取事件名称
function _eventNames() {
  let events = this._events;

  // 不存在则初始化
  if (isVoid(events)) {
    this._events = Object.create(null);
    events = this._events;
  }
  
  return Reflect.ownKeys(events);
}

// 事件触发
function _emit(type, ...args) {
  const events = this._events;
  
  if (isVoid(events)) {
    this._events = Object.create(null);
    return false;
  }

  const listeners = events[type],
	    listenerLength = listeners?.length;

  if (!listenerLength) return false;

  for (let i = 0; i < listenerLength; i++) {
    const listenerCallback = listeners[i];

    // 是否为箭头函数(箭头函数指向 undefined)
    // listenerCallback.apply(isFunction(listenerCallback) ? this : undefined, args);
    listenerCallback.apply(this, args);
  }

  return true;
}

function _listeners(eventName) {
  const events = this._events;

  if (isVoid(events)) return [];

  const evListener = events[eventName];

  if (isVoid(evListener)) return [];


  return arrayClone(evListener);
}

function _listenerCount(eventName) {
  const listeners = _listeners.call(this, eventName);
  return listeners.length;
}

// 绑定事件
EventEmitter.prototype.on = function (type, listener) {
  _addListener.call(this, type, listener, false);
  return this;
}


// 将事件处理函数 添加在 开头
EventEmitter.prototype.prependListener = function (type, listener) {
  _addListener.call(this, type, listener, true);
  return this;
}

// 绑定事件, 只执行一次

EventEmitter.prototype.once = function (type, listener) {
  const oldListener = listener;
  listener = (...args) => {
    oldListener(...args); // 执行一次
    this.off(type, listener); // 删除
  }

  listener.innerListener = oldListener;

  _addListener.call(this, type, listener, false);
  return this;
}


// 绑定事件, 只执行一次, 并放在开头
EventEmitter.prototype.prependOnceListener = function (type, listener) {
  const oldListener = listener;

  listener = (...args) => {
    oldListener(...args); // 执行一次
    this.off(type, listener); // 删除
  }

  listener.innerListener = oldListener;
  
  _addListener.call(this, type, listener, true);
  return this;
}

// 取消绑定
EventEmitter.prototype.off = function (type, listener) {
  _removeListener.call(this, type, listener);
  return this;
}

// 事件触发
EventEmitter.prototype.emit = function (type, ...args) {
  return _emit.call(this, type, ...args);
}

// 获取所有事件名称
EventEmitter.prototype.eventNames = function () {
  return _eventNames.call(this);
}

// 删除多个 监听器
EventEmitter.prototype.removeAllListeners = function (...args) {
  if(isArray(args[0])) {
    args = args[0];
  }
  _removeListeners.apply(this, args);
  return this;
}

// 返回一个事件队列
EventEmitter.prototype.rawListeners = function (eventName) {
  return _listeners.call(this, eventName);
}

// 获取对应事件队列  处理函数个数
EventEmitter.prototype.listenerCount = function (eventName) {
  return _listenerCount.call(this, eventName);
}

// utils

// 检查是否为函数
function checkListener(listener) {
  if (typeof listener !== 'function') {
    throw new TypeError('listener is not type function');
  }
}

// 判断是否为 undefined
function isVoid(value) {
  return value == undefined;
}

// 克隆数组
function arrayClone(arr) {
  return arr.slice();
}

// 判断是否为数组
function isArray(arr) {
  return Array.isArray(arr);
}

module.exports = EventEmitter;

总结

  1. events 模块是 NodeJS 内置 发布与订阅模块
  2. 我们常用的模块有: on, once, off, emit, prependListener, prependOnceListener, rawListeners, eventNames, listenerCount, removeAllListeners