Skip to main content

如何实现订阅模式

¥How to implement a subscription model

默认情况下,即使另一端没有注册事件处理程序,事件也会通过线路发送。

¥By default, events are sent over the wire even if there is no registered event handler on the other side.

note

你可以使用捕获所有监听器捕获那些丢失的事件处理程序:

¥You can catch those missing event handlers with a catch-all listener:

socket.onAny((event) => {
if (socket.listeners(event).length === 0) {
console.log(`missing handler for event ${event}`);
}
});

参考:onAny() 方法

¥Reference: onAny() method

要仅接收特定事件的列表(例如,如果应用的一部分只需要少数事件),你可以实现订阅模型:

¥To only receive a list of specific events (for example, if a part of your application only needs a handful of events), you can implement a subscription model:

客户端

¥Client

const subscriptions = [];

function subscribe(topic) {
subscriptions.push(topic);
if (socket.connected) {
socket.emit("subscribe", [topic]);
}
}

function unsubscribe(topic) {
const i = subscriptions.indexOf(topic);
if (i !== -1) {
subscriptions.splice(i, 1);
if (socket.connected) {
socket.emit("unsubscribe", topic);
}
}
}

// restore the subscriptions upon reconnection
socket.on("connect", () => {
if (subscriptions.length && !socket.recovered) {
socket.emit("subscribe", subscriptions);
}
});

subscribe("foo");

服务器

¥Server

io.on("connection", (socket) => {
socket.on("subscribe", (topics) => {
socket.join(topics);
});

socket.on("unsubscribe", (topic) => {
socket.leave(topic);
});

// send an event only to clients that have shown interest in the "foo" topic
io.to("foo").emit("foo");
});

补充注意

¥Additional notes

订阅列表

¥List of subscriptions

我们可以在客户端使用 ES6 Set 进行订阅:

¥We could have used a ES6 Set for the subscriptions on the client side:

const subscriptions = new Set();

function subscribe(topic) {
subscriptions.add(topic);
if (socket.connected) {
socket.emit("subscribe", [topic]);
}
}

function unsubscribe(topic) {
const deleted = subscriptions.delete(topic);
if (deleted && socket.connected) {
socket.emit("unsubscribe", topic);
}
}

// restore the subscriptions upon reconnection
socket.on("connect", () => {
if (subscriptions.size) {
socket.emit("subscribe", [...subscriptions]);
}
});

哪个更干净(例如,不需要处理重复的订阅),但如果你需要定位 旧平台,则需要填充程序。

¥Which is cleaner (no need to handle duplicate subscriptions, for example) but would require a polyfill if you need to target old platforms.

连接状态恢复

¥Connection state recovery

在 "connect" 处理程序中:

¥In the "connect" handler:

socket.on("connect", () => {
if (subscriptions.length && !socket.recovered) {
socket.emit("subscribe", subscriptions);
}
});

!socket.recovered 条件与 连接状态恢复功能 相关。

¥The !socket.recovered condition is related to the Connection state recovery feature.

如果连接状态成功恢复,则订阅(服务器端的房间)将自动恢复。

¥If the connection state was successfully recovered, then the subscriptions (the rooms on the server side) will be automatically restored.

参考:socket.recovered attribute

¥Reference: socket.recovered attribute