node 訂閱釋出及實現

江小魚發表於2018-06-09

釋出訂閱模式 觀察者模式

釋出訂閱是主動的,來個例子,我有一個女朋友,要包,要車。等我有錢了,買包買車。這個過程中,我要先訂閱買包買車,等有錢了在釋出。

觀察者被動執行。我先觀察你,你有變化了,我在變化,有點兒類似vue watch

釋出訂閱

例子1

先把想幹的事情存起來,當真的發生時候,依次將陣列中的內容執行。對應關係如下:

{
  有錢:  [買包,買車]  
  女生失戀:  [哭,喝酒]  
}
複製程式碼

程式碼實現:

let EventEmitter = require('events');
let util = require('util');
// 定義一個類
function Girl() { 

}

// 繼承共有方法
util.inherits(Girl, EventEmitter);

let girl = new Girl();

let cry = () => {
  console.log('哭');
}
let drink = () => {
  console.log('喝酒');
}

// 繫結對應方法
girl.on('女生失戀', cry);
girl.on('女生失戀', drink);

// 預設情況下是不執行的
// 觸發執行
girl.emit('女生失戀');

複製程式碼

模擬實現釋出訂閱

  • 定義一個類
<!--新建檔案events.js-->
// 定義一個類
function EventEmitter() {

}

// 匯出
module.exports = EventEmitter;

複製程式碼
  • 定義好 on 和 emit 方法
// 定義一個類
function EventEmitter() {

}

// 訂閱
EventEmitter.prototype.on = function() {

}

// 釋出
EventEmitter.prototype.emit = function() {

}

// 匯出
module.exports = EventEmitter;

複製程式碼
  • 首先先維護一個物件,物件對應 { '女生失戀': ['喝酒fn', '哭fn'] }
// 定義一個類
function EventEmitter() {
  // 維護一個物件
  // {
  //   '女生失戀': ['哭', '喝酒']
  // }
  this._events = {};
}

// 訂閱
EventEmitter.prototype.on = function(type, callback) {
  // 如果取不到_events 預設給空物件
  if (!this._events){
    this._events = {};
  }
  // 物件中存在
  if (this._events[type]) {
    this._events[type].push(callback);
  } else {
    this._events[type] = [callback];
  }
}
複製程式碼
  • 當執行 emit 時候將陣列中的函式依次執行
// 釋出
EventEmitter.prototype.emit = function(type) {
  // 存在陣列
  if (this._events[type]) {
    this._events[type].forEach(fn => {
      // 此處的 fn 就是 喝酒, 哭
      fn();
    });
  }
}
複製程式碼
  • 解綁方法
EventEmitter.prototype.removeListener = function(type, callback) {
  // 找到陣列中對應方法移除。
  if (this._events[type]) {
    this._events[type] = this._events[type].filter(fn => {
      return fn != callback;
    })
  }
}

// 外界呼叫
// 解綁事件
girl.removeListener('女生失戀', cry);
複製程式碼
  • 新增 newListener 方法獲取當前監聽什麼事件名字。

當前監聽的如果不是 newListener 時候。執行 newListener 方法把當前 type 傳遞給回撥函式,這樣外界就可以獲取 type 了

// 訂閱
EventEmitter.prototype.on = function(type, callback) {
  if (type !== 'newListener') {
    this._events['newListener'].forEach(fn => {
      fn(type);
    })
  }
  // 如果取不到_events 預設給空物件
  if (!this._events){
    this._events = {};
  }
  // 物件中存在
  if (this._events[type]) {
    this._events[type].push(callback);
  } else {
    this._events[type] = [callback];
  }
}

// 外界呼叫
girl.on('newListener', (type) => {
  console.log(type);
})
複製程式碼
  • 其他方法
// 預設最大監聽
EventEmitter.defaultMaxListener = 10;

// 設定最大監聽
EventEmitter.prototype.setMaxListeners = function(count) {
  this._count = count;
}
// 獲取最大監聽
EventEmitter.prototype.getMaxListeners = function(count) {
  return this._count || EventEmitter.defaultMaxListener;
}
// 獲取 eventName
EventEmitter.prototype.eventNames = function() {
  return Object.keys(this._events);
}
// 獲取監聽方法
EventEmitter.prototype.listeners = function(type) {
  return this._events[type];
}

// 移除所有監聽
EventEmitter.prototype.removeAllListeners = function(type) {
  if (type) {
   return this._events[type] = [];
  };
  this._events = {};
}
複製程式碼

程式碼:

event.js

// 定義一個類
function EventEmitter() {
  // 維護一個物件
  // {
  //   '女生失戀': ['哭', '喝酒']
  //   'newListener': []
  // }
  this._events = {};
}

// 訂閱
EventEmitter.prototype.addListener = EventEmitter.prototype.on = function(type, callback) {
  // 如果取不到_events 預設給空物件
  if (!this._events) {
    this._events = Object.create(null);
  }
  if (type !== 'newListener' && this._events['newListener'] && this._events['newListener'].length) {
    this._events['newListener'].forEach(fn => {
      fn(type);
    })
  }
  // 物件中存在
  if (this._events[type]) {
    this._events[type].push(callback);
  } else {
    this._events[type] = [callback];
  }
  // 如果超限制提示錯誤
  if (this._events[type].length === this.getMaxListeners()) {
    console.warn('memory link detected');
  }
}

// 釋出
EventEmitter.prototype.emit = function(type, ...args) {
  // 存在陣列
  if (this._events[type]) {
    this._events[type].forEach(fn => {
      // 此處的 fn 就是 喝酒, 哭
      fn(...args);
    });
  }
}

EventEmitter.prototype.removeListener = function(type, callback) {
  // 找到陣列中對應方法移除。
  if (this._events[type]) {
    this._events[type] = this._events[type].filter(fn => {
      return fn != callback && fn.l !== callback;
    })
  }
}
// 預設最大監聽
EventEmitter.defaultMaxListener = 10;

// 設定最大監聽
EventEmitter.prototype.setMaxListeners = function(count) {
  this._count = count;
}
// 獲取最大監聽
EventEmitter.prototype.getMaxListeners = function(count) {
  return this._count || EventEmitter.defaultMaxListener;
}
// 獲取 eventName
EventEmitter.prototype.eventNames = function() {
  return Object.keys(this._events);
}
// 獲取監聽方法
EventEmitter.prototype.listeners = function(type) {
  return this._events[type];
}

// 移除所有監聽
EventEmitter.prototype.removeAllListeners = function(type) {
  if (type) {
   return this._events[type] = [];
  };
  this._events = {};
}

// once 先繫結 wrap 函式當執行完後從陣列中刪除。
EventEmitter.prototype.once = function(type, callback) {
  // 新增一個包裹函式。
  let wrap = (...args) => {
    callback(...args);
    this.removeListener(type, wrap);
  }
  // 將callback儲存在 wrap.l上
  wrap.l = callback;
  this.on(type, wrap);
}

// 匯出
module.exports = EventEmitter;

複製程式碼

呼叫:

let EventEmitter = require('./events');
let util = require('util');
// 定義一個類
function Girl() { 
  // EventEmitter.call(this);
}

// 繼承共有方法
util.inherits(Girl, EventEmitter);

let girl = new Girl();

let cry = (a, b) => {
  console.log('哭', a, b);
}
let drink = (a, b) => {
  console.log('喝酒', a, b);
}

// girl.setMaxListeners(1);

// console.log(girl.getMaxListeners());

girl.on('newListener', (type) => {
  // console.log(type, '哈哈哈');
})

// 繫結對應方法
girl.once('女生失戀', cry);
// girl.on('女生失戀', drink);

// 解綁事件
// girl.removeListener('女生失戀', cry);

// 預設情況下是不執行的
// 觸發執行
girl.emit('女生失戀', 1, 2);
girl.emit('女生失戀');
girl.emit('女生失戀');
// 獲取最大監聽
// console.log(EventEmitter.defaultMaxListener);

// 獲取 eventNames [ 'newListener', '女生失戀' ]
// console.log(girl.eventNames());

console.log(girl.listeners('女生失戀'));
複製程式碼

相關文章