Skip to content
文档章节

发布订阅模式

发布订阅模式在前端开发中非常常见。前端有很多异步场景,比如按钮点击事件,消息广播等都有应用。

发布订阅消息

假如我们要实现一个发布订阅平台消息的操作,我们该如何做呢 ?

js
function Notice() {}

Notice.clientList = {};

Notice.subscribe = function (key, fn) {
  if (!this.clientList[key]) this.clientList[key] = [];
  let fnIndex = this.clientList[key].indexOf(fn);
  if (fnIndex === -1) {
    this.clientList[key].push(fn);
  }
};

Notice.publish = function (...args) {
  let key = args.shift();

  if (!key || !this.clientList[key]) return;

  this.clientList[key].forEach(fn => {
    fn.apply(this, args);
  });
};

Notice.remove = function (key, fn) {
  var fns = this.clientList[key];
  if (!fns) return;

  // 移除所有订阅
  if (!fn) {
    fns.length = 0;
    return;
  }

  let fnIndex = fns.findIndex(item => item === fn);
  if (fnIndex > -1) fns.splice(fnIndex, 1);
};

// 订阅者
Notice.subscribe('computer', (...args) => console.log('computer subfn1', args));
Notice.subscribe('computer', (...args) => console.log('computer subfn2', args));
Notice.subscribe('science', (...args) => console.log('science subfn3', args));

// 消息发布者
Notice.publish('computer', 'js', 'css');

setTimeout(() => {
  Notice.publish('science', 'math');
}, 2000);

上面的代码中可以看到,消息订阅者需要先发起订阅请求,等待消息发布者发布消息,才能接收到通知。那么能不能没有订阅者的时候,也能发布消息,等到出现订阅者的时候,将消息传给订阅者呢?

js
Notice.subscribe = function (key, fn) {
  // 如果有缓存的订阅消息,则接收
  if (this.msgCache[key]) {
    this.msgCache[key].forEach(args => {
      fn.apply(this, args);
    });
  }

  if (!this.clientList[key]) this.clientList[key] = [];
  let fnIndex = this.clientList[key].indexOf(fn);
  if (fnIndex === -1) {
    this.clientList[key].push(fn);
  }
};

Notice.publish = function (...args) {
  let key = args.shift();

  if (!key) return;
  // 没有订阅消息,缓存当前消息
  if (!this.clientList[key]) {
    if (!this.msgCache[key]) this.msgCache[key] = [];
    this.msgCache[key].push([...args]);
    return;
  }

  this.clientList[key].forEach(fn => {
    fn.apply(this, args);
  });
};

// 消息发布者
Notice.publish('computer', 'js', 'css');

setTimeout(() => {
  Notice.publish('science', 'math');
}, 2000);

// 订阅者
Notice.subscribe('computer', (...args) => console.log('computer subfn1', args));
Notice.subscribe('computer', (...args) => console.log('computer subfn2', args));
Notice.subscribe('science', (...args) => console.log('science subfn3', args));