fix: fix reconnection logic
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "tap-sdk-js",
|
"name": "tap-sdk-js",
|
||||||
"version": "1.1.3",
|
"version": "1.2.3",
|
||||||
"description": "",
|
"description": "",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export type WSEvent = {
|
|||||||
export type WSState = {
|
export type WSState = {
|
||||||
socket: WebSocket | null;
|
socket: WebSocket | null;
|
||||||
connected: boolean;
|
connected: boolean;
|
||||||
|
connecting: boolean;
|
||||||
retries: number;
|
retries: number;
|
||||||
emitter: EventEmitter<WSEvent>;
|
emitter: EventEmitter<WSEvent>;
|
||||||
};
|
};
|
||||||
@@ -28,49 +29,87 @@ export function createWebSocketManager(config: WSConfig) {
|
|||||||
const state: WSState = {
|
const state: WSState = {
|
||||||
socket: null,
|
socket: null,
|
||||||
connected: false,
|
connected: false,
|
||||||
|
connecting: false,
|
||||||
retries: 0,
|
retries: 0,
|
||||||
emitter: new EventEmitter(),
|
emitter: new EventEmitter(),
|
||||||
};
|
};
|
||||||
|
|
||||||
function connect() {
|
function connect() {
|
||||||
return new Promise<void>((resolve) => {
|
state.connecting = true;
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
const socket = new WebSocket(config.url);
|
const socket = new WebSocket(config.url);
|
||||||
socket.on("open", () => {
|
|
||||||
|
const onOpen = () => {
|
||||||
|
socket.off("open", onOpen);
|
||||||
|
socket.off("error", onError);
|
||||||
|
socket.off("close", onClose);
|
||||||
|
|
||||||
state.socket = socket;
|
state.socket = socket;
|
||||||
state.connected = true;
|
state.connected = true;
|
||||||
|
state.connecting = false;
|
||||||
state.retries = 0;
|
state.retries = 0;
|
||||||
state.emitter.emit("open");
|
state.emitter.emit("open");
|
||||||
resolve();
|
resolve();
|
||||||
});
|
|
||||||
|
|
||||||
|
// Re-attach persistent listeners
|
||||||
socket.on("message", (msg) => {
|
socket.on("message", (msg) => {
|
||||||
state.emitter.emit("message", msg.toString());
|
state.emitter.emit("message", msg.toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("error", (err) => {
|
socket.on("error", (err) => {
|
||||||
if (state.connected) state.emitter.emit("error", err);
|
state.emitter.emit("error", err);
|
||||||
else state.emitter.emit("connectionError", err);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("close", () => {
|
socket.on("close", () => {
|
||||||
state.emitter.emit("close");
|
|
||||||
state.connected = false;
|
state.connected = false;
|
||||||
|
state.emitter.emit("close");
|
||||||
|
retry();
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onError = (err: Error) => {
|
||||||
|
socket.off("open", onOpen);
|
||||||
|
socket.off("error", onError);
|
||||||
|
socket.off("close", onClose);
|
||||||
|
state.connecting = false;
|
||||||
|
state.emitter.emit("connectionError", err);
|
||||||
|
reject(err);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClose = () => {
|
||||||
|
socket.off("open", onOpen);
|
||||||
|
socket.off("error", onError);
|
||||||
|
socket.off("close", onClose);
|
||||||
|
state.connecting = false;
|
||||||
|
const err = new Error("Connection closed before open");
|
||||||
|
state.emitter.emit("connectionError", err);
|
||||||
|
reject(err);
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.on("open", onOpen);
|
||||||
|
socket.on("error", onError);
|
||||||
|
socket.on("close", onClose);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function retry() {
|
function retry() {
|
||||||
|
if (state.connected || state.connecting) return;
|
||||||
|
state.retries++;
|
||||||
setTimeout(
|
setTimeout(
|
||||||
() => {
|
() => {
|
||||||
connect().catch(() => retry());
|
start();
|
||||||
state.retries++;
|
|
||||||
},
|
},
|
||||||
backoffDelay(state.retries, config.backoffBaseMs),
|
backoffDelay(state.retries - 1, config.backoffBaseMs),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function start() {
|
async function start() {
|
||||||
await connect().catch(() => retry());
|
if (state.connected || state.connecting) return;
|
||||||
|
try {
|
||||||
|
await connect();
|
||||||
|
} catch (e) {
|
||||||
|
retry();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function send(message: string) {
|
function send(message: string) {
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ export function createClient(userConfig: ZakoTapOptions): ZakoTapClientHandle {
|
|||||||
socket.events.on("connectionError", (err) =>
|
socket.events.on("connectionError", (err) =>
|
||||||
emitter.emit("connectionError", err),
|
emitter.emit("connectionError", err),
|
||||||
);
|
);
|
||||||
|
socket.events.on("close", () => emitter.emit("close"));
|
||||||
|
|
||||||
socket.events.on("message", (content) => {
|
socket.events.on("message", (content) => {
|
||||||
const data = JSON.parse(content);
|
const data = JSON.parse(content);
|
||||||
|
|||||||
@@ -26,6 +26,11 @@ export type TapEvents = {
|
|||||||
* Event called on initial connection error.
|
* Event called on initial connection error.
|
||||||
*/
|
*/
|
||||||
connectionError: (e: Error) => any;
|
connectionError: (e: Error) => any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event called on connection close.
|
||||||
|
*/
|
||||||
|
close: () => any;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createEmitter = () => new EventEmitter<TapEvents>();
|
export const createEmitter = () => new EventEmitter<TapEvents>();
|
||||||
|
|||||||
Reference in New Issue
Block a user