nodejs之EventEmitter實現

Node.js 所有的異步 I/O 操作在完成時都會發送一個事件到事件隊列。

Node.js 裏面的許多對象都會分發事件:一個 net.Server 對象會在每次有新連接時觸發一個事件, 一個 fs.readStream 對象會在文件被打開的時候觸發一個事件。 所有這些產生事件的對象都是 events.EventEmitter 的實例。

以下簡單實現:

function EventEmitter() {
  this.events = {}
  this.counts = 0
  this.maxNum = 10
  // 內置事件:newListener -> 事件在添加新監聽器時被觸發。removeListener -> 事件在接除監聽器時被觸發。
  this.innerEvent = {
    NEWLISTENER: 'newListener',
    REMOVELISTENER: 'removeListener'
  }
}

// 爲指定事件添加一個監聽器到監聽器數組的尾部。
function addListener(eventName, callback) {
  if (typeof callback !== 'function') return

  if (!this.events[eventName]) {
    if (!this._isInnerEvent(eventName)) {
      this.events[eventName] = [{ type: 'on', callback }]
      this.counts++
      if (this.counts > this.maxNum) {
        console.warn(`目前監聽器數量:${this.counts}個,監聽器已經超過${this.maxNum}個`)
      }
      if (this.events[this.innerEvent.NEWLISTENER]) {
        this.emit(this.innerEvent.NEWLISTENER, eventName)
      }
    } else {
      this.events[eventName] = { type: 'on', callback }
    }
  } else {
    this.events[eventName].push({ type: 'on', callback })
  }
}

EventEmitter.prototype = {
  _toString: function (obj) {
    return Object.prototype.toString.call(obj).slice(8, -1)
  },

  _isInnerEvent: function (eventName) {
    return !!this.innerEvent[eventName.toUpperCase()]
  },

  addListener: addListener,
  on: addListener,

  // 按監聽器的順序執行執行每個監聽器,如果事件有註冊監聽返回 true,否則返回 false。
  emit: function () {
    let arg = Array.prototype.slice.call(arguments)
    let eventName = arg[0]
    let params = arg.slice(1)

    if (!this._isInnerEvent(eventName)) {
      if (this._toString(this.events[eventName]) === 'Array' && this.events[eventName].length) {
        this.events[eventName].forEach(event => {
          let { type, callback } = event
          callback.apply(null, params)
          if (type === 'once') {
            this.events[evtName].splice(index, 1)
          }
        })
        return true
      }
      return false
    } else {
      this.events[eventName].callback.apply(null, params)
      if (this.events[eventName].type === 'once') {
        delete this.events[eventName]
      }
    }
  },

  // 爲指定事件註冊一個單次監聽器,即 監聽器最多隻會觸發一次,觸發後立刻解除該監聽器。
  once: function (eventName, callback) {
    if (typeof callback !== 'function') return

    if (!this.events[eventName]) {
      if (!this._isInnerEvent(eventName)) {
        this.events[eventName] = [{ type: 'once', callback }]
        this.counts++
        if (this.counts > this.maxNum) {
          console.warn(`目前監聽器數量:${this.counts}個,監聽器已經超過${this.maxNum}個`)
        }
        if (this.events[this.innerEvent.NEWLISTENER]) {
          this.emit(this.innerEvent.NEWLISTENER, eventName)
        }
      } else {
        this.events[eventName] = { type: 'once', callback }
      }
    } else {
      this.events[eventName].push({ type: 'once', callback })
    }
  },

  // 移除指定事件的某個監聽器,監聽器必須是該事件已經註冊過的監聽器。
  removeListener: function (eventName, callback) {
    if (this._toString(this.events[eventName]) === 'Array') {
      let _events = this.events[eventName].concat()

      for (let i = 0; i < _events.length; i++) {
        if (_events[i].callback === callback) {
          this.events[eventName].splice(i, 1)
          return
        }
      }
    }
  },

  // 移除所有事件的所有監聽器, 如果指定事件,則移除指定事件的所有監聽器。
  removeAllListeners: function (eventName) {
    if (eventName) {
      if (this.events[eventName]) {
        delete this.events[eventName]
        if (!this._isInnerEvent(eventName)) {
          this.counts--
          if (this.events[this.innerEvent.REMOVELISTENER]) {
            this.emit(this.innerEvent.REMOVELISTENER, eventName)
          }
        }
      }
    } else {
      this.events = {}
      this.counts = 0
    }
  },

  // 默認情況下, EventEmitters 如果你添加的監聽器超過 10 個就會輸出警告信息。 setMaxListeners 函數用於提高監聽器的默認限制的數量。
  setMaxListeners: function (num) {
    this.maxNum = num
  },

  // 返回指定事件的監聽器數組。
  listeners: function (eventName) {
    if (this._toString(this.events[eventName]) === 'Array') {
      let _events = this.events[eventName].concat()
      let newArray = []
      _events.forEach(item => {
        newArray.push(item.callback)
      })
      return newArray
    }
  },

  // 返回指定事件的監聽器數量
  listenerCount: function (eventName) {
    if (this._toString(this.events[eventName]) === 'Array') {
      return this.events[eventName].length
    }
  }
}

var eventEmit = new EventEmitter()

eventEmit.on('newListener', function newListener(eventName) {
  console.log('>>>>newListener ---', eventName)
})
eventEmit.on('removeListener', function removeListener(eventName) {
  console.log('>>>>removeListener ---', eventName)
})

console.log(eventEmit)

function event1() {
  console.log('event1')
}
eventEmit.on('event', event1)

eventEmit.on('event1', event1)

eventEmit.on('event', function event2() {
  console.log('event2')
})

eventEmit.emit('event')
eventEmit.emit('event1')

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章