发布订阅

2021-06-19 16:39:00 Javascript 大约 2 分钟

软件架构 (opens new window)中,发布订阅是一种消息 (opens new window)范式 (opens new window),消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话)可能存在。同样的,订阅者可以表达对一个或多个类别的兴趣,只接收感兴趣的消息,无需了解哪些发布者(如果有的话)存在。

image-20210620002123396

优点:

  • 低耦合;
  • 灵活;

缺点:

  • 无法确保消息被触发或者触发几次;

# 实现

首先我们定义一个 发布订阅的 类;

class PubSub {
  constructor() {
    this.events = {};
  }

  // 订阅者
  $on(eventNames, callback) {
    if (eventNames instanceof Array) {
      for (let i = 0; i < eventNames.length; i++) {
        if (eventNames[i] in this.events) {
          this.events[eventNames[i]].push(callback);
        } else {
          this.events[eventNames[i]] = [callback];
        }
      }
    } else {
      if (eventNames in this.events) {
        this.events[eventNames].push(callback);
      } else {
        this.events[eventNames] = [callback];
      }
    }
  }

  // 发布者
  $emit(eventNames, data) {
    if (eventNames instanceof Array) {
      for (let i = 0; i < eventNames.length; i++) {
        if (eventNames[i] in this.events) {
          this.events[eventNames[i]].forEach(item => {
            item.call(this, data)
          })
        } else {
          console.log('事件未注册:%o', eventNames[i])
        }
      }
    } else {
      if (eventNames in this.events) {
        this.events[eventNames].forEach(item => {
          item.apply(this,data)
        })
      } else {
        console.log('事件未注册:%o', eventNames)
      }
    }
  }

  // 取消订阅
  $unSub(names, callback) {
    if (names instanceof Array) {
      for (let i = 0; i < names.length; i++) {
        if (names[i] in this.events) {
          const callbacks = this.events[names[i]].filters(item => item !== callback);
          if (callbacks.length) {
            this.events[names[i]] = callbacks;
          } else {
            delete this.events[names[i]]
          }
        }
      }
    } else {
      if (names in this.events) {
        const callbacks = this.events[names].filter(item => item !== callback);
        if (callbacks.length) {
          this.events[names] = callbacks;
        } else {
          delete this.events[names]
          console.log('事件已删除:%o', names)
        }
      }
    }
  }
}

module.exports = PubSub;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

# 订阅单个消息

// 订阅单个消息;
const PubSub = require('./PubSub.js');
const pubSub = new PubSub();
function event_1(res) {
  console.log('我执行了:%o', res)
}

pubSub.$on('event_1', event_1);

pubSub.$emit('event_1', '今天是周末');

pubSub.$unSub('event_1', event_1);

pubSub.$emit('event_1', '今天周末');

// 我执行了:'今天是周末'
// 事件已删除:'event_1'
// 事件未注册:'event_1'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 订阅多个消息

const PubSub = require('./PubSub.js');
const pubSub = new PubSub();
function event_1(res) {
  console.log('event_1 我执行了:%o', res)
}

function event_2(res) {
  console.log('event_2 我执行了:%o', res)
}

pubSub.$on(['event_1', 'event_2'], event_1);
pubSub.$on('event_2' , event_2);
pubSub.$emit(['event_1', 'event_2'], '今天是周一');
console.log('----------')
pubSub.$unSub('event_1', event_1);
console.log('----------')
pubSub.$emit(['event_2'], '今天是周一');
pubSub.$unSub('event_2', event_2);
console.log('----------')
pubSub.$emit(['event_2'], '今天是周一');

// event_1 我执行了:'今天是周一'
// event_1 我执行了:'今天是周一'
// event_2 我执行了:'今天是周一'
// ----------
// 事件已删除:'event_1'
// ----------
// event_1 我执行了:'今天是周一'
// event_2 我执行了:'今天是周一'
// ----------
// event_1 我执行了:'今天是周一'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

提示

上面的PubSup类中,是多对多的关系;发布者可以同时发布多条消息,同时订阅者,也可以根据自己的"兴趣"订阅多个类别;

上次编辑于: 2023年7月4日 09:36