Frontend Development 19 min read

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.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Implementing a WebSocket Client with Automatic Reconnection and Heartbeat in TypeScript/JavaScript

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.

frontendtypescriptJavaScriptWebSocketheartbeatreconnection
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.