【JavaScript】JSでPub/Subメッセージングを実装する

2022/09/30に公開

Point

共通化された「メッセージ」という単位を利用することで、PublisherとSubscriberを疎結合に保つことができます。PublisherとSubscriberは、それぞれ「データの作り手」と「データの受け手」と言えます。

実装

TypeScriptで簡潔に実装してみます。

Message

メッセージは共通化されたオブジェクトです。場合によっては「イベント」や「アクション」などと言い換えられるかもしれません。
多くの場合、メッセージにはトピックとデータが含まれます。ここでは、メッセージの「TYPE」と「DATA」という形としています。

interface Message {
    type: "TYPE_A" | "TYPE_B";
    data: Data;
}

Subscriber

Subscriberはメッセージを受け取り、対応する何らかの処理を行います。Messageオブジェクトを受け取ることができるインターフェースを実装します。

type Subscriber =  (msg: Message) => any;

例えば、以下のような関数です。

const logger: Subscriber = (msg) => console.log(msg.type);

PubSubService

サービスクラスは、複数のSubscriberを管理します。また、購読の解除(unsubscribe)も簡易に実装しています。

登録された関数をコレクションとして管理します。

class PubSubService {
  private subscribers = new Set<Subscriber>();

  subscribe = (fn: Subscriber) => this.subscribers.add(fn);
  unsubscribe = (fn: Subscriber) => this.subscribers.delete(fn);
}

Publish

さらに、メッセージの配信機能を追加します。引数として受け取ったメッセージを、登録されている全ての関数に渡します。

class PubSubService {
  private subscribers = new Set<Subscriber>();

  subscribe = (fn: Subscriber) => this.subscribers.add(fn);
  unsubscribe = (fn: Subscriber) => this.subscribers.delete(fn);
  // 追加
  publish = (msg: Message) => this.subscribers.forEach((fn) => fn(msg));
}

実行してみる

// ロガー(Subscriber)
const logger: Subscriber = (msg) => console.log("LOG:: ", msg.type, msg.data);

// メッセージングサービス
const pubsub = new PubSubService();
// ロガーを登録
pubsub.subscribe(logger);
// メッセージを配信
pubsub.publish({type: "TYPE_A", data: 123})
実行結果
>>> LOG:: TYPE_A 123

おわりに

共通化された「メッセージ」という単位を利用することで、PublisherとSubscriberを疎結合に保つことができます。

さらに、特定のトピック(タイプ)のみ購読するなど、機能追加することでメッセージ配信先をコントロールすることも可能です。


参考

http://itdoc.hitachi.co.jp/manuals/link/cosmi_v0870/APKC/EU070377.HTM

Discussion