From ef855e6ba2a4304320d1b51ae31c3beb8766b57f Mon Sep 17 00:00:00 2001 From: Waldemar Schlegel Date: Thu, 15 Dec 2022 16:19:02 +0100 Subject: [PATCH] fix: add timeout method to remote socket (#4558) The RemoteSocket interface, which is returned when the client is connected on another Socket.IO server of the cluster, was lacking the `timeout()` method. Syntax: ```js const sockets = await io.fetchSockets(); for (const socket of sockets) { if (someCondition) { socket.timeout(1000).emit("some-event", (err) => { if (err) { // the client did not acknowledge the event in the given delay } }); } } ``` Related: https://github.com/socketio/socket.io/issues/4595 --- lib/broadcast-operator.ts | 51 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/lib/broadcast-operator.ts b/lib/broadcast-operator.ts index 0426e4d02a..0dbeebb42d 100644 --- a/lib/broadcast-operator.ts +++ b/lib/broadcast-operator.ts @@ -7,6 +7,7 @@ import type { EventNames, EventsMap, TypedEventBroadcaster, + DecorateAcknowledgements, DecorateAcknowledgementsWithTimeoutAndMultipleResponses, AllButLast, Last, @@ -20,7 +21,9 @@ export class BroadcastOperator private readonly adapter: Adapter, private readonly rooms: Set = new Set(), private readonly exceptRooms: Set = new Set(), - private readonly flags: BroadcastFlags = {} + private readonly flags: BroadcastFlags & { + expectSingleResponse?: boolean; + } = {} ) {} /** @@ -232,7 +235,10 @@ export class BroadcastOperator const timer = setTimeout(() => { timedOut = true; - ack.apply(this, [new Error("operation has timed out"), responses]); + ack.apply(this, [ + new Error("operation has timed out"), + this.flags.expectSingleResponse ? null : responses, + ]); }, this.flags.timeout); let expectedServerCount = -1; @@ -246,7 +252,10 @@ export class BroadcastOperator responses.length === expectedClientCount ) { clearTimeout(timer); - ack.apply(this, [null, responses]); + ack.apply(this, [ + null, + this.flags.expectSingleResponse ? null : responses, + ]); } }; @@ -476,10 +485,44 @@ export class RemoteSocket this.data = details.data; this.operator = new BroadcastOperator( adapter, - new Set([this.id]) + new Set([this.id]), + new Set(), + { + expectSingleResponse: true, // so that remoteSocket.emit() with acknowledgement behaves like socket.emit() + } ); } + /** + * Adds a timeout in milliseconds for the next operation. + * + * @example + * const sockets = await io.fetchSockets(); + * + * for (const socket of sockets) { + * if (someCondition) { + * socket.timeout(1000).emit("some-event", (err) => { + * if (err) { + * // the client did not acknowledge the event in the given delay + * } + * }); + * } + * } + * + * // note: if possible, using a room instead of looping over all sockets is preferable + * io.timeout(1000).to(someConditionRoom).emit("some-event", (err, responses) => { + * // ... + * }); + * + * @param timeout + */ + public timeout(timeout: number) { + return this.operator.timeout(timeout) as BroadcastOperator< + DecorateAcknowledgements, + SocketData + >; + } + public emit>( ev: Ev, ...args: EventParams