简述

发布-订阅模式将订阅者和发布者解耦,订阅者不用一直“询问”发布者事件是否发生,而是由发布者自由决定向订阅者发送通知。

实现

以购物为例,买家需要向卖家购买一件商品,但是库存已经没有了,买家想要知道什么时候有货,那么买家需要隔一段时间就去问卖家有没有货,如果采用了发布-订阅模式,买家只需要将联系方式留给卖家,当卖家进货时,有卖家统一通知买家,从而减少查询次数。

初步实现

代码

class Shop {
  constructor() {
    this.repository = {}
  }

  // 订阅
  $on(goods, callback) {
    if (!this.repository[goods]) {
      this.repository[goods] = []
    }
    this.repository[goods].push(callback)
  }

  // 发布
  $emit(goods) {
    if (this.repository[goods] && this.repository[goods].length) {
      this.repository[goods].forEach(cb => cb(goods))
    }
    else {
      console.log('该商品没有客户订阅')
    }
  }

  // 取消订阅
  $removeSubscribe(goods, callback) {
    if (this.repository[goods]) {
      this.repository[goods] = this.repository[goods].filter(
        cb => cb != callback
      )
      console.log('取消订阅成功')
    }
  }

  // 一次性订阅
  $once(goods, callback) {
    const fn = (goods) => {
      callback(goods)
      this.$removeSubscribe(goods, fn)
    }
    this.$on(goods, fn)
  }
}

const shop = new Shop()
shop.$on('电脑', (goods) => {
  console.log(`${goods}货到啦!`)
})

shop.$once('鼠标', (goods) => {
  console.log(`${goods}货到啦!`)
})

shop.$emit('电脑')
shop.$emit('鼠标')
shop.$emit('鼠标')
class Shop {
  constructor() {
    this.repository = {}
  }

  // 订阅
  $on(goods, callback) {
    if (!this.repository[goods]) {
      this.repository[goods] = []
    }
    this.repository[goods].push(callback)
  }

  // 发布
  $emit(goods) {
    if (this.repository[goods] && this.repository[goods].length) {
      this.repository[goods].forEach(cb => cb(goods))
    }
    else {
      console.log('该商品没有客户订阅')
    }
  }

  // 取消订阅
  $removeSubscribe(goods, callback) {
    if (this.repository[goods]) {
      this.repository[goods] = this.repository[goods].filter(
        cb => cb != callback
      )
      console.log('取消订阅成功')
    }
  }

  // 一次性订阅
  $once(goods, callback) {
    const fn = (goods) => {
      callback(goods)
      this.$removeSubscribe(goods, fn)
    }
    this.$on(goods, fn)
  }
}

const shop = new Shop()
shop.$on('电脑', (goods) => {
  console.log(`${goods}货到啦!`)
})

shop.$once('鼠标', (goods) => {
  console.log(`${goods}货到啦!`)
})

shop.$emit('电脑')
shop.$emit('鼠标')
shop.$emit('鼠标')

输出

电脑货到啦!
鼠标货到啦!
取消订阅成功
该商品没有客户订阅

这里只是初步实现,还有很多地方需要完善。

实现订阅一组事件

只需要修改$on函数即可

$on(goods, callback) {
    if(Array.isArray(goods)) {
        goods.forEach(goodsName => {
            this._push(goodsName, callback);
        })
    } else {
        this._push(goods, callback);
    }
}

_push(goods, callback) {
    if (!this.repository[goods]) {
        this.repository[goods] = [];
    }
    this.repository[goods].push(callback);
}
$on(goods, callback) {
    if(Array.isArray(goods)) {
        goods.forEach(goodsName => {
            this._push(goodsName, callback);
        })
    } else {
        this._push(goods, callback);
    }
}

_push(goods, callback) {
    if (!this.repository[goods]) {
        this.repository[goods] = [];
    }
    this.repository[goods].push(callback);
}