Skip to content

Commit 44fef65

Browse files
docs: example with subscription model
Related: socketio/socket.io#4816
1 parent c20e4ac commit 44fef65

File tree

2 files changed

+124
-0
lines changed

2 files changed

+124
-0
lines changed

src/pages/get-started/index.md

+1
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,4 @@ A slightly more complex chat application, introducing [middlewares](/docs/v4/mid
3838
- [upload a file](/how-to/upload-a-file)
3939
- [register a global middleware](/how-to/register-a-global-middleware)
4040
- [build a basic Socket.IO client](/how-to/build-a-basic-client)
41+
- [implement a subscription model](/how-to/implement-a-subscription-model)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
---
2+
title: How to implement a subscription model
3+
---
4+
5+
# How to implement a subscription model
6+
7+
By default, events are sent over the wire even if there is no registered event handler on the other side.
8+
9+
:::note
10+
11+
You can catch those missing event handlers with a catch-all listener:
12+
13+
```js
14+
socket.onAny((event) => {
15+
if (socket.listeners(event).length === 0) {
16+
console.log(`missing handler for event ${event}`);
17+
}
18+
});
19+
```
20+
21+
Reference: [`onAny()` method](/docs/v4/client-api/#socketonanycallback)
22+
23+
:::
24+
25+
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:
26+
27+
## Client
28+
29+
```js
30+
const subscriptions = [];
31+
32+
function subscribe(topic) {
33+
subscriptions.push(topic);
34+
if (socket.connected) {
35+
socket.emit("subscribe", [topic]);
36+
}
37+
}
38+
39+
function unsubscribe(topic) {
40+
const i = subscriptions.indexOf(topic);
41+
if (i !== -1) {
42+
subscriptions.splice(i, 1);
43+
if (socket.connected) {
44+
socket.emit("unsubscribe", topic);
45+
}
46+
}
47+
}
48+
49+
// restore the subscriptions upon reconnection
50+
socket.on("connect", () => {
51+
if (subscriptions.length && !socket.recovered) {
52+
socket.emit("subscribe", subscriptions);
53+
}
54+
});
55+
56+
subscribe("foo");
57+
```
58+
59+
## Server
60+
61+
```js
62+
io.on("connection", (socket) => {
63+
socket.on("subscribe", (topics) => {
64+
socket.join(topics);
65+
});
66+
67+
socket.on("unsubscribe", (topic) => {
68+
socket.leave(topic);
69+
});
70+
71+
// send an event only to clients that have shown interest in the "foo" topic
72+
io.to("foo").emit("foo");
73+
});
74+
```
75+
76+
## Additional notes
77+
78+
### List of subscriptions
79+
80+
We could have used a ES6 Set for the subscriptions on the client side:
81+
82+
```js
83+
const subscriptions = new Set();
84+
85+
function subscribe(topic) {
86+
subscriptions.add(topic);
87+
if (socket.connected) {
88+
socket.emit("subscribe", [topic]);
89+
}
90+
}
91+
92+
function unsubscribe(topic) {
93+
const deleted = subscriptions.delete(topic);
94+
if (deleted && socket.connected) {
95+
socket.emit("unsubscribe", topic);
96+
}
97+
}
98+
99+
// restore the subscriptions upon reconnection
100+
socket.on("connect", () => {
101+
if (subscriptions.size) {
102+
socket.emit("subscribe", [...subscriptions]);
103+
}
104+
});
105+
```
106+
107+
Which is cleaner (no need to handle duplicate subscriptions, for example) but would require a polyfill if you need to target [old platforms](https://caniuse.com/mdn-javascript_builtins_set).
108+
109+
### Connection state recovery
110+
111+
In the "connect" handler:
112+
113+
```js
114+
socket.on("connect", () => {
115+
if (subscriptions.length && !socket.recovered) {
116+
socket.emit("subscribe", subscriptions);
117+
}
118+
});
119+
```
120+
121+
The `!socket.recovered` condition is related to the [Connection state recovery feature](/docs/v4/connection-state-recovery).
122+
123+
If the connection state was successfully recovered, then the subscriptions (the rooms on the server side) will be automatically restored.

0 commit comments

Comments
 (0)