pfan123 / Articles

经验文章
169 stars 25 forks source link

EventEmitter 理解 #16

Open pfan123 opened 7 years ago

pfan123 commented 7 years ago

EventEmitter 事件发射器是一种发布,订阅模式,是event 模块提供的一个对象,用来注册事件可触发事件。Node 中的异步操作都是基于EventEmitter 来实现的。

EventEmitter主要API

EventEmitter使用方式

const events = require('events');

const emitter = new events.EventEmitter();

// 绑定sayHi事件,可以绑定多个同名事件,触发时会顺序触发
emitter.on('sayHi', function(someone){
    console.log("我是", someone)
})
emitter.on('sayHi', function(someone){
    console.log("我就是", someone)
})

// 触发sayHi事件
emitter.emit('sayHi', 'pfan');

// 我是pfan
// 我就是pfan

继承获取事件对象的方法

const util = require('util');

const events = require('events');

// 创建自定义对象
let Cat = function (name) {
    this.name = name;
}

// 继承events.EventEmitter
util.inherits(Cat, events.EventEmitter);

// 创建自定义对象实例
let Tom = new Cat('Tom');

// 绑定sayHiTo事件
Tom.on('sayHi', function(someone){
    // this指向实例Tom
    console.log(this.name," sayHiTo ", someone)
})

Tom.emit('sayHiTo', 'pfan')

// 输出

// Tom sayHiTo pfan

Node.js中大部分的模块,都继承自Event模块,来异步操作处理如request、stream

常见Event Emitter工具库实现

可以采用现有的成熟框架,通过继承即可食用:

Component: component/emitter Bower or standalone: Wolfy87/EventEmitter

EventEmitter的实现原理

EventEmitter实现并不难,可以实现一个简单的版本,加深理解。具备以下基本功能:

function Emitter(){
  this.events = {}
}

Emitter.prototype.on = function(type, listener){
  // 在事件对象中加入新的属性
  // 确保新的属性以数组的形式保存
  this.events[type] = this.events[type] || [];
  // 在数组中加入事件处理函数
  this.events[type].push(listener);
}

Emitter.prototype.off = function(type, listener){
    if(this.events && this.events[type]){
        delete this.events[type]
        listener(...arguments)
    }
}

Emitter.prototype.once = function(type, listener){
    let self = this
    this.on(type, function(){
        self.off(type)
        listener(...arguments)
    })
}

Emitter.prototype.emit = function(type, arg){
  if(this.events[type]) {// 如果事件对象中含有该属性
    this.events[type].forEach(function(listener){
      listener(arg)
    })
  }
}
module.exports = Emitter;

component/emitter 源码分析

/**
 * 使用 `Emitter` 方式
 */
var Emitter = require('emitter');
var emitter = new Emitter;
emitter.emit('something');

on(event, listener)
为指定事件注册一个监听器,接受一个字符串 event 和一个回调函数。

emit(event, [arg1], [arg2], [...])
按参数的顺序执行每个监听器,如果事件有注册监听返回 true,否则返回 false。

/**
 * 使用 `Emitter` 方式
 */

/**
 * 设置导出 `Emitter` 类
 */

if (typeof module !== 'undefined') {
  module.exports = Emitter;
}

/**
 * Initialize a new `Emitter`.
 *
 * @api public
 */

function Emitter(obj) {
  if (obj) return mixin(obj);
};

/**
 * Mixin the emitter properties.
 * 浅拷贝 Emitter.prototype
 * @param {Object} obj
 * @return {Object}
 * @api private
 */

function mixin(obj) {
  for (var key in Emitter.prototype) {
    obj[key] = Emitter.prototype[key];
  }
  return obj;
}

/**
 * Listen on the given `event` with `fn`.
 * 事件只执行一次
 * @param {String} event
 * @param {Function} fn
 * @return {Emitter}
 * @api public
 */

Emitter.prototype.on =
Emitter.prototype.addEventListener = function(event, fn){
  this._callbacks = this._callbacks || {};
  (this._callbacks['$' + event] = this._callbacks['$' + event] || [])
    .push(fn);
  return this;
};

/**
 * Adds an `event` listener that will be invoked a single
 * time then automatically removed.
 *
 * @param {String} event
 * @param {Function} fn
 * @return {Emitter}
 * @api public
 */

Emitter.prototype.once = function(event, fn){
  function on() {
    this.off(event, on);
    fn.apply(this, arguments);
  }

  on.fn = fn;
  this.on(event, on);
  return this;
};

/**
 * Remove the given callback for `event` or all
 * registered callbacks.
 * 移除注册的监听器
 * @param {String} event
 * @param {Function} fn
 * @return {Emitter}
 * @api public
 */

Emitter.prototype.off =
Emitter.prototype.removeListener =
Emitter.prototype.removeAllListeners =
Emitter.prototype.removeEventListener = function(event, fn){
  this._callbacks = this._callbacks || {};

  // all
  if (0 == arguments.length) {
    this._callbacks = {};
    return this;
  }

  // specific event
  var callbacks = this._callbacks['$' + event];
  if (!callbacks) return this;

  // remove all handlers
  if (1 == arguments.length) {
    delete this._callbacks['$' + event];
    return this;
  }

  // remove specific handler
  var cb;
  for (var i = 0; i < callbacks.length; i++) {
    cb = callbacks[i];
    if (cb === fn || cb.fn === fn) {
      callbacks.splice(i, 1);
      break;
    }
  }
  return this;
};

/**
 * Emit `event` with the given args.
 * 事件发射告诉监听器开始执行
 * @param {String} event
 * @param {Mixed} ...
 * @return {Emitter}
 */

Emitter.prototype.emit = function(event){
  this._callbacks = this._callbacks || {};
  var args = [].slice.call(arguments, 1)
    , callbacks = this._callbacks['$' + event];

  if (callbacks) {
    callbacks = callbacks.slice(0);
    for (var i = 0, len = callbacks.length; i < len; ++i) {
      callbacks[i].apply(this, args);
    }
  }

  return this;
};

/**
 * Return array of callbacks for `event`.
 * 返回监听
 * @param {String} event
 * @return {Array}
 * @api public
 */

Emitter.prototype.listeners = function(event){
  this._callbacks = this._callbacks || {};
  return this._callbacks['$' + event] || [];
};

/**
 * Check if this emitter has `event` handlers.
 * 检测是否含有监听器
 * @param {String} event
 * @return {Boolean}
 * @api public
 */

Emitter.prototype.hasListeners = function(event){
  return !! this.listeners(event).length;
};

Component: component/emitter Bower or standalone: Wolfy87/EventEmitter Node.js EventEmitter 理解Event Emitter 深入浅出Node.js(四):Node.js的事件机制