Implementing a WebSocket Client with Automatic Reconnection and Heartbeat in TypeScript/JavaScript
This article explains how to build a WebSocket client that supports automatic reconnection after network loss and periodic heartbeat messages, provides a drop‑in API compatible with the native WebSocket interface, and includes complete TypeScript and JavaScript implementations with detailed code examples.
Introduction
WebSocket is a must‑know technology for front‑end development. The article demonstrates how to encapsulate the native WebSocket API to add automatic reconnection and heartbeat functionality while keeping the usage identical to the original API.
Core Advantages
The wrapper uses the same method signatures as the native WebSocket, so developers face zero learning cost and can start using it immediately.
Basic Wrapper Demo
import WebSocketClient from "./WebSocketClient";
const ws = new WebSocketClient('ws://localhost:3200');
ws.connect();
ws.onclose(() => {});
ws.onerror(() => {});
ws.onmessage(() => {});
ws.onopen(() => {});
ws.close();Backend Service Creation (Node)
Install the ws library and create a simple WebSocket server that pushes data every ten seconds.
npm install ws
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 3200 });
console.log('服务运行在http://localhost:3200/');
wss.on('connection', (ws) => {
console.log('[服务器]:客官您来了~里边请');
ws.send(`[websocket云端]您已经连接云端!数据推送中!`);
let index = 1;
const interval = setInterval(() => {
ws.send(`[websocket]数据推送第${index}次`);
index++;
}, 1000 * 10);
ws.on('close', () => {
clearInterval(interval);
console.log('[服务器]:客官下次再来呢~');
});
});Front‑end Test
Use the wrapper in a front‑end project to connect to the server and observe automatic heartbeat logs and reconnection behavior when the server is stopped.
import { WebSocketClient } from '@/utils/dataDispatcher/WebSocketClient';
const ws = new WebSocketClient('ws://localhost:3200');
ws.connect();
ws.onclose(() => {});
ws.onerror(() => {});
ws.onmessage(() => {});
ws.onopen(() => {});Technical Implementation
Basic Framework
export class WebSocketClient {
private url = '';
private socket: WebSocket | null = null;
private reconnectAttempts = 0;
private maxReconnectAttempts = 5;
private reconnectInterval = 10000; // 10 seconds
constructor(url: string) {
this.url = url;
}
public send(message: string): void {
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
this.socket.send(message);
} else {
console.error('[WebSocket] 未连接');
}
}
public connect(): void {
if (this.reconnectAttempts === 0) {
console.log(`初始化连接中... ${this.url}`);
}
if (this.socket && this.socket.readyState === WebSocket.OPEN) return;
this.socket = new WebSocket(this.url);
this.socket.onopen = (event) => {
this.reconnectAttempts = 0;
this.startHeartbeat();
console.log(`连接成功,等待服务端数据推送[onopen]... ${this.url}`);
};
this.socket.onmessage = (event) => {
// handle message
this.startHeartbeat();
};
this.socket.onclose = (event) => {
if (!this.stopWs) this.handleReconnect();
};
this.socket.onerror = (event) => {
if (!this.stopWs) this.handleReconnect();
};
}
private handleReconnect(): void {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
console.log(`尝试重连... (${this.reconnectAttempts}/${this.maxReconnectAttempts}) ${this.url}`);
setTimeout(() => this.connect(), this.reconnectInterval);
} else {
console.log(`最大重连失败,终止重连: ${this.url}`);
}
}
private heartbeatInterval = 1000 * 30;
private heartbeatTimer?: NodeJS.Timeout;
private stopWs = false;
private startHeartbeat(): void {
if (this.stopWs) return;
if (this.heartbeatTimer) this.closeHeartbeat();
this.heartbeatTimer = setInterval(() => {
if (this.socket) {
this.socket.send(JSON.stringify({ type: 'heartBeat', data: {} }));
console.log('送心跳数据...');
} else {
console.error('[WebSocket] 未连接');
}
}, this.heartbeatInterval);
}
private closeHeartbeat(): void {
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = undefined;
}
public close(): void {
if (this.socket) {
this.stopWs = true;
this.socket.close();
this.socket = null;
}
this.closeHeartbeat();
}
}Event Dispatcher (Base Class)
export class EventDispatcher {
private listeners: { [type: string]: Function[] } = {};
protected addEventListener(type: string, listener: Function) {
if (!this.listeners[type]) this.listeners[type] = [];
if (this.listeners[type].indexOf(listener) === -1) this.listeners[type].push(listener);
}
protected removeEventListener(type: string) {
this.listeners[type] = [];
}
protected dispatchEvent(type: string, data: any) {
const listenerArray = this.listeners[type] || [];
if (!listenerArray.length) return;
listenerArray.forEach((listener) => listener.call(this, data));
}
}Conclusion
The provided wrapper fully supports automatic reconnection and heartbeat, works with both TypeScript and plain JavaScript, and retains the native WebSocket API signature, making it easy to integrate into existing front‑end projects. Developers can further customize reconnection intervals and heartbeat payloads as needed.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.