Skip to content

Commit f629062

Browse files
authored
feat: trigger reconnect on emit if not connected (#162)
* feat: trigger reconnect on emit if not connected * change order of setting connected = true and handling the connect event. * Await initializing method on reconnect * Asyncify the initialization tests * Add test and simplify emit reconnect code * Rename initializingPromise to initializing
1 parent daa9780 commit f629062

File tree

3 files changed

+40
-12
lines changed

3 files changed

+40
-12
lines changed

source/event_hub.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ export class EventHub {
189189
* @return {Boolean}
190190
*/
191191
isConnected(): boolean {
192-
return this._socketIo?.isConnected() || false;
192+
return this._socketIo?.socket.connected || false;
193193
}
194194

195195
/**

source/simple_socketio.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export default class SimpleSocketIOClient {
5555
private packetQueue: string[] = [];
5656
private reconnectionAttempts: number = 0;
5757
private reconnecting: boolean = false;
58+
initializing: Promise<void>;
5859

5960
// Added socket object with connected, open reconnect and transport properties to match current API
6061
// The old socket-io client uses both a connected and open property that are interchangeable
@@ -129,7 +130,7 @@ export default class SimpleSocketIOClient {
129130
this.heartbeatTimeoutMs = heartbeatTimeoutMs;
130131
this.apiUser = apiUser;
131132
this.apiKey = apiKey;
132-
this.initializeWebSocket();
133+
this.initializing = this.initializeWebSocket();
133134
}
134135
/**
135136
* Fetches the session ID from the ftrack server.
@@ -235,10 +236,10 @@ export default class SimpleSocketIOClient {
235236
}
236237
this.reconnecting = false;
237238
this.reconnectionAttempts = 0; // Reset reconnection attempts
238-
this.handleEvent("connect", {});
239-
this.flushPacketQueue();
240239
// Set connected property to true
241240
this.socket.connected = true;
241+
this.handleEvent("connect", {});
242+
this.flushPacketQueue();
242243
}
243244
/**
244245
* Handles WebSocket closing
@@ -313,10 +314,11 @@ export default class SimpleSocketIOClient {
313314
const dataString = eventData ? `:::${JSON.stringify(payload)}` : "";
314315
const packet = `${PACKET_TYPES.event}${dataString}`;
315316

316-
if (this.webSocket?.readyState === WebSocket.OPEN) {
317+
if (this.isConnected()) {
317318
this.webSocket.send(packet);
318319
} else {
319320
this.packetQueue.push(packet);
321+
this.reconnect();
320322
}
321323
}
322324
/**
@@ -404,15 +406,16 @@ export default class SimpleSocketIOClient {
404406
* @private
405407
* @param randomizedDelay
406408
*/
407-
private attemptReconnect(): void {
409+
private async attemptReconnect(): Promise<void> {
410+
await this.initializing;
408411
// Check if already connected or if active reconnection attempt ongoing.
409412
if (this.socket.connected || this.reconnecting) {
410413
return;
411414
}
412415
this.reconnecting = true;
413416
this.reconnectionAttempts++;
414-
this.initializeWebSocket();
415417
this.reconnectTimeout = undefined;
418+
this.initializing = this.initializeWebSocket();
416419
this.reconnect();
417420
}
418421
/**

test/simple_socketio.test.js

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -410,19 +410,43 @@ describe("Tests using SimpleSocketIOClient", () => {
410410
`${PACKET_TYPES.event}${expectedDataString}`,
411411
);
412412
});
413+
test("emit triggers a reconnect if not connected", () => {
414+
client.webSocket = createWebSocketMock();
415+
client.webSocket.readyState = WebSocket.CLOSED;
416+
417+
const reconnectSpy = vi.spyOn(client, "reconnect");
418+
419+
const eventName = "testEvent";
420+
const eventData = { foo: "bar" };
421+
422+
client.emit(eventName, eventData);
423+
424+
expect(reconnectSpy).toHaveBeenCalledTimes(1);
425+
426+
const expectedPayload = {
427+
name: eventName,
428+
args: [eventData],
429+
};
430+
const expectedDataString = `:::${JSON.stringify(expectedPayload)}`;
431+
expect(client.packetQueue).toContainEqual(
432+
`${PACKET_TYPES.event}${expectedDataString}`,
433+
);
434+
435+
reconnectSpy.mockRestore();
436+
});
413437
describe("Reconnection tests", () => {
414-
test("attemptReconnect method initialises websocket again", () => {
438+
test("attemptReconnect method initialises websocket again", async () => {
415439
client.initializeWebSocket = vi.fn();
416440

417-
client.attemptReconnect();
441+
await client.attemptReconnect();
418442

419443
expect(client.initializeWebSocket).toHaveBeenCalledTimes(1);
420444
});
421445

422-
test("attemptReconnect method increments attempts count", () => {
446+
test("attemptReconnect method increments attempts count", async () => {
423447
const initialAttempts = client.reconnectionAttempts;
424448

425-
client.attemptReconnect();
449+
await client.attemptReconnect();
426450

427451
expect(client.reconnectionAttempts).toBe(initialAttempts + 1);
428452
});
@@ -456,7 +480,7 @@ describe("Tests using SimpleSocketIOClient", () => {
456480
// Reconnect should not be called yet
457481
expect(client.attemptReconnect).toHaveBeenCalledTimes(0);
458482
});
459-
test("reconnect method exponentially increase delay for every attempt, stopping at the max value", () => {
483+
test("reconnect method exponentially increase delay for every attempt, stopping at the max value", async () => {
460484
const originalRandom = Math.random;
461485
Math.random = vi.fn().mockReturnValue(1);
462486
vi.useFakeTimers();
@@ -468,6 +492,7 @@ describe("Tests using SimpleSocketIOClient", () => {
468492
const expectedMaxDelay = expectedMinDelay * 1.5;
469493
client.reconnecting = false; // Since it never gets to the actual fail state triggered by
470494
client.reconnect();
495+
await client.initializing;
471496
vi.advanceTimersByTime(expectedMaxDelay + 1);
472497
expect(client.attemptReconnect).toHaveBeenCalledTimes(i + 1);
473498
}

0 commit comments

Comments
 (0)