Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
96 lines (73 sloc) 2.09 KB

发布订阅模式

事件发布/订阅模式 (PubSub) 在异步编程中帮助我们完成更松的解耦,甚至在 MVC、MVVC 的架构中以及设计模式中也少不了发布-订阅模式的参与。

优点:在异步编程中实现更深的解耦

缺点:如果过多的使用发布订阅模式,会增加维护的难度

实现一个发布订阅模式

var Event = function() {
  this.obj = {}
}

Event.prototype.on = function(eventType, fn) {
  if (!this.obj[eventType]) {
    this.obj[eventType] = []
  }
  this.obj[eventType].push(fn)
}

Event.prototype.emit = function() {
  var eventType = Array.prototype.shift.call(arguments)
  var arr = this.obj[eventType]
  for (let i = 0; i < arr.length; i++) {
    arr[i].apply(arr[i], arguments)
  }
}

var ev = new Event()

ev.on('click', function(a) { // 订阅函数
  console.log(a) // 1
})

ev.emit('click', 1)          // 发布函数

订阅函数逻辑一定要优先于发布函数吗

考虑以下场景:

$.ajax('', () => {
  // 异步订阅函数逻辑
})

// 在其他地方执行发布函数,此时并不能保证执行发布函数的时候,订阅函数已经执行

我们需要实现这样的逻辑:

var ev = new Event()
ev.emit('click', 1)

ev.on('click', function(a) {
  console.log(a) // 1
})

目标明确后,来着手实现它:

var Event = function() {
  this.obj = {}
  this.cacheList = []
}

Event.prototype.on = function(eventType, fn) {
  if (!this.obj[eventType]) {
    this.obj[eventType] = []
  }
  this.obj[eventType].push(fn)

  for (let i = 0; i < this.cacheList.length; i++) {
    this.cacheList[i]()
  }
}

Event.prototype.emit = function() {
  const arg = arguments
  const that = this
  function cache() {
    var eventType = Array.prototype.shift.call(arg)
    var arr = that.obj[eventType]
    for (let i = 0; i < arr.length; i++) {
      arr[i].apply(arr[i], arg)
    }
  }
  this.cacheList.push(cache)
}

以上代码实现思路就是把原本在 emit 里触发的函数存到 cacheList,再转交到 on 中触发。从而实现了发布函数先于订阅函数执行。