Explanation of super simple publish and subscribe model

publish-subscribe model

Publish-subscribe pattern and Observer pattern are two common design patterns for handling events and communication. In this article, we will gradually build a fully functional EventEmitter, and through this process we will gain a deep understanding of the publish-subscribe model.

concise version

We build a simple publish-subscribe model:

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

  // 订阅事件
  on(event, listener) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(listener);
  }

  // 卸载事件
  off(event, listener) {
    if (this.events[event]) {
      this.events[event] = this.events[event].filter(l => l !== listener);
    }
  }

  // 发布事件
  emit(event, data) {
    if (this.events[event]) {
      this.events[event].forEach(listener => listener(data));
    }
  }
}

We created a EventEmitter class, which includes on, off and emitMethods, respectively used for subscribing events, unloading events and publishing events.

Code flow explanation:
  • on(event, listener): This function is used to subscribe to events. If a specific event does not exist in the event list, we create a new event array and add the incoming listener to that array.
  • off(event, listener): This function is used for unloading events. We first check if the event list contains a specific event, and if so, remove the incoming listener from the event array using the filter method.
  • emit(event, data): This function is used to publish events. If the event list contains a specific event, we iterate through the event array and fire each listener, passing the event data.

Upgraded version: Add one-time call

We introduced a one-time subscription, also known as theonce method.

// ...

  // 一次性订阅
  once(event, listener) {
    const onceWrapper = data => {
      listener(data);
      this.off(event, onceWrapper);
    };
    this.on(event, onceWrapper);
  }
Process explanation:
  • once(event, listener): This function allows you to subscribe to events one-time, that is, automatically uninstall the subscription after the event is triggered once. It internally creates a wrapper function onceWrapper that executes the one-time listener when the event is triggered, and then automatically unloads the subscription to ensure that the listener is only executed once.

Advanced version: supports continuous calls

Let ourEventEmitter support continuous calls so that we can execute multiple methods continuously in a chain.

class EventEmitter {
  // ...

  // 订阅事件,支持连续调用
  on(event, listener) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(listener);
    return this;
  }

  // 卸载事件,支持连续调用
  off(event, listener) {
    if (this.events[event]) {
      this.events[event] = this.events[event].filter(l => l !== listener);
    }
    return this;
  }

  // 一次性订阅,支持连续调用
  once(event, listener) {
    const onceWrapper = data => {
      listener(data);
      this.off(event, onceWrapper);
    };
    this.on(event, onceWrapper);
    return this;
  }
}
Process explanation:
  • on(event, listener), off(event, listener), once(event, listener): In this step, we added return this; statement, which means that each method TheEventEmitter instance will be returned after the call. This allows us to call different methods consecutively in a chain of calls, thus improving the readability of the code.

The returns of these three functions allow us to call them consecutively like this:

eventEmitter
  .on('event1', listener1)
  .on('event2', listener2)
  .once('event3', listener3)
  .off('event4', listener4);

With consecutive calls, we can easily perform multiple operations in a chain.

Full version: Add event cache

In the fourth step, we introduced the event cache, allowing viewing of subscribed events.

// ...

  // 获取事件列表
  getEvents() {
    return Object.keys(this.events);
  }
}
Code flow explanation:
  • getEvents(): This function allows you to get a list of subscribed events. It returns an array containing the names of all subscribed events.

The difference between publish-subscribe mode and observer mode

Main differences:
  • The publish-subscribe model is more flexible and allows many-to-many relationships between multiple publishers and subscribers. The publisher does not need to know the existence of the subscribers. It usually uses a mediator to handle event distribution.
  • The observer pattern is usually a one-to-many relationship, a subject object can have multiple observers, but the interface of the observer is clearly defined. The topic object needs to maintain a list of observers and notify observers when the state changes.

Observer pattern example:

// 主题
class Subject {
  constructor() {
    this.observers = [];
  }

  addObserver(observer) {
    this.observers.push(observer);
  }

  removeObserver(observer) {
    const index = this.observers.indexOf(observer);
    if (index !== -1) {
      this.observers.splice(index, 1);
    }
  }

  notify(message) {
    this.observers.forEach(observer => observer.update(message));
  }
}

// 观察者
class Observer {
  update(message) {
    console.log(`Received message: ${message}`);
  }
}

const subject = new Subject();
const observer1 = new Observer();
const observer2 = new Observer();

subject.addObserver(observer1);
subject.addObserver(observer2);
subject.notify("Hello, observers!");

In our introduction above, the publish-subscribe pattern uses a mediator to handle the publication and subscription of events, while the observer pattern is a one-to-many relationship, and the subject object directly notifies the observer.

Summarize

We introduced the basic publish-subscribe pattern by gradually building an event model that supports continuous invocation. At the same time, the difference between the publish-subscribe mode and the observer mode is briefly introduced. The publish-subscribe mode is more flexible, supports many-to-many relationships, and is suitable for more scenarios, while the observer mode is a one-to-many relationship and clearly defines the interface of the observer. Finally, I hope this article will help everyone understand the publish-subscribe model.

Guess you like

Origin blog.csdn.net/yuleiming21/article/details/133798996