JS实现发布订阅模式

278 阅读1分钟

什么是发布订阅模式

一种一对多的消息发布模式,发布者会将消息广播给所有订阅者

可以类比于微信公众号

用户(Subscriber)订阅公众号(Publisher)之后,发布者发布文章(Message),用户接收文章

Publish-subscribe pattern using a message broker

注意点

  1. 需要先订阅,再发布

    类似于mitt中,先on,再emit

  2. 最大的特点就是:解耦

    每个子系统独立管理,系统消息不会因为少了几个订阅者而崩溃

  3. 通信是单向的,如果要双向通信,请考虑请求/回复模式

  4. 增加系统复杂度

    订阅者不知道是在哪里发布的(代码阅读性极差,代码维护成本高)

    发布者不知道订阅者是否接收到消息

JS实现发布订阅模式

class EventMitter {
  // 初始化事件列表
  constructor() {
    this.list = [];
  }
  // 订阅
  on(type, handler) {
    (this.list[type] || (this.list[type] = [])).push(handler);
    return this;
  }
  // 取消订阅
  off(type, handler) {
    let fns = this.list[type];
    // 不存在则直接退出
    if (!fns) {
      return false;
    }
    if (!handler) {
      // 没有指定则全部删除,清空数组
      fns && (fns.length = 0);
    } else {
      // 删除特定的handler
      for (let i = 0; i < fns.length; i++) {
        if (fns[i] == handler) {
          fns.splice(i, 1);
          break;
        }
      }
    }
    return this;
  }
  // 只执行一次
  once(type, handler) {
    let _this = this;
    function f() {
      _this.off(type, f);
      handler.apply();
    }
    this.on(type, f);
    return this;
  }
  // 发布
  emit(type, data) {
    const fns = this.list[type] || [];
    fns.forEach((fn)=>{
      fn(data);
    })
    return this;
  }
}

// test code
const bus = new EventMitter();
function f1(e){
  console.log('f1',e);
}
function f2(){
  console.log('f2');
}
function f3(e){
  console.log('f3',e);
}
function f4(e){
  console.log('f4',e);
}

bus.on('f1',f1).on('f2',f2).on('f3',f3).once('f4',f4);
bus.emit('f1',1).emit('f2',2).emit('f3',3).emit('f4',4);
bus.off('f1',f1).off('f2',f1).off('f2',f2);
bus.on('f2',f2);
bus.on('f2',f2);
bus.emit('f2');
bus.off('f2');
bus.emit('f1');
bus.emit('f2');
bus.emit('f3');
bus.emit('f4');

/*
f1 1
f2
f3 3
f4 undefined
f2
f2
f3 undefined
*/