nodejs 의 EventEmitter 실현

13981 단어
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')

좋은 웹페이지 즐겨찾기