本文へ移動

WebSocket と SSE ​

fromCallback と fromEvent でイベントをストリームとして受信します。キューの上限は64件で、満杯になったら最も古い値を捨てます。

ts
import { fromCallback, fromEvent } from "mogami-stream";

export function websocketMessages(socket: WebSocket, signal: AbortSignal) {
  return fromCallback<MessageEvent>(
    (observer) => {
      const message = (event: MessageEvent) => observer.next(event);
      const error = (event: Event) => observer.error(event);
      const close = () => observer.complete();
      socket.addEventListener("message", message);
      socket.addEventListener("error", error);
      socket.addEventListener("close", close);
      return () => {
        socket.removeEventListener("message", message);
        socket.removeEventListener("error", error);
        socket.removeEventListener("close", close);
      };
    },
    { capacity: 64, overflow: "drop-oldest" },
  ).iterate({ signal });
}

export function sseMessages(events: EventSource, signal: AbortSignal) {
  return fromEvent<MessageEvent>(events, "message", {
    capacity: 64,
    overflow: "drop-oldest",
    onDrop(info) {
      console.warn("Dropped SSE messages in this execution:", info.count);
    },
  }).iterate({ signal });
}

どちらのアダプタも終了時にリスナーを解除します。

満杯時に失敗させる場合は overflow: "error"、新しい値を捨てる場合は "drop-newest" を指定します。

接続を消費して閉じる ​

接続の所有者が finally で閉じます。SSE は渡された signal が abort されるまで受信を続け、WebSocket のアダプタは close イベントでも終了します。

ts
export async function receiveEvents(url: string, signal: AbortSignal) {
  const connection = new EventSource(url);
  try {
    for await (const event of sseMessages(connection, signal)) {
      console.log(event.data);
    }
  } finally {
    connection.close();
  }
}