In order to tackle this issue, my approach would involve creating a Class
specifically designed to manage multiple "fancys".
To provide a complete solution, here is the response I shared in the comments yesterday - it covers all the requirements outlined in the question.
class Fancy {
static fancies = {};
static add(match_id, socket) {
if (!Fancy.fancies[match_id]) {
Fancy.fancies[match_id] = new Fancy(match_id);
}
Fancy.fancies[match_id].subscribe(socket);
}
subscribers = [];
constructor(match_id) {
this.match_id = match_id;
this.checkInterval = setInterval(async () => {
const fancy = await callapi(this.match_id);
this.subscribers.forEach((socket) =>
socket.emit(`fancy_${this.match_id}`, fancy)
);
});
}
subscribe(socket) {
if (!this.subscribers.find((sock) => sock === socket)) {
this.subscribers.push(socket);
}
}
}
socket.on('fancy' , ({match_id}) => Fancy.add(match_id, socket));
It's important to note that even though this is a class
, only the static add
method needs to be utilized.
However, it should be mentioned that this implementation does not account for disconnections or allowing users to opt-out of a "fancy" as indicated in the comment.
Expanding upon this code involves incorporating a few static methods and instance methods:
Introducing a static remove
method which serves as the counterpart to the existing add
method, and a disconnect
static method to remove the socket from all instances.
For instance methods, including an unsubscribe
, start
, and stop
method could enhance the functionality.
class Fancy {
static fancies = {};
static add(match_id, socket) {
if (!Fancy.fancies[match_id]) {
Fancy.fancies[match_id] = new Fancy(match_id);
}
Fancy.fancies[match_id].subscribe(socket);
}
static remove(match_id, socket) {
Fancy.fancies[match_id]?.unsubscribe(socket);
}
static disconnect(socket) {
Fancy.fancies.forEach(fancy => fancy.unsubscribe(socket));
}
// instance properties/methods
subscribers = [];
constructor(match_id) {
this.match_id = match_id;
}
start() {
if (!this.checkInterval) {
this.checkInterval = setInterval(async () => {
const fancy = await callapi(this.match_id);
this.subscribers.forEach((socket) =>
socket.emit(`fancy_${this.match_id}`, fancy)
);
});
}
}
stop() {
clearInterval(this.setInterval);
this.checkInterval = null;
}
unsubscribe(socket) {
this.subscribers = this.subscribers.filter(sock => sock !== socket);
if (this.subscribers.length === 0) {
this.stop();
}
}
subscribe(socket) {
if (!this.subscribers.find((sock) => sock === socket)) {
this.subscribers.push(socket);
if (this.subscribers.length === 1) {
this.start();
}
}
}
}
socket.on('fancy' , ({match_id}) => Fancy.add(match_id, socket));
socket.on('unfancy' , ({match_id}) => Fancy.remove(match_id, socket));
socket.on('close', () => Fancy.disconnect(socket));
An alternative approach to the stop
/start
methods could involve keeping the interval running but only triggering the API request when there are active "subscribers".
This adjustment simplifies the code by adding just one instance method (unsubscribe).
Here is a modified version implementing this strategy:
class Fancy {
static fancies = {};
static add(match_id, socket) {
if (!Fancy.fancies[match_id]) {
Fancy.fancies[match_id] = new Fancy(match_id);
}
Fancy.fancies[match_id].subscribe(socket);
}
static remove(match_id, socket) {
Fancy.fancies[match_id]?.unsubscribe(socket);
}
static disconnect(socket) {
Fancy.fancies.forEach(fancy => fancy.unsubscribe(socket));
}
// instance properties/methods
subscribers = [];
constructor(match_id) {
this.match_id = match_id;
this.checkInterval = setInterval(async () => {
if (this.subscribers.length) {
const fancy = await callapi(this.match_id);
this.subscribers.forEach((socket) =>
socket.emit(`fancy_${this.match_id}`, fancy)
);
}
});
}
unsubscribe(socket) {
this.subscribers = this.subscribers.filter(sock => sock !== socket);
}
subscribe(socket) {
if (!this.subscribers.find((sock) => sock === socket)) {
this.subscribers.push(socket);
}
}
}
Personally, I favor the last solution as having an interval that remains idle when there are no subscribers does not impose any performance issues.