javascript
examples
examples.js⚡javascript
/**
* 16.6 WebSockets and Real-Time Communication - Examples
*/
// ============================================
// BASIC WEBSOCKET CONNECTION
// ============================================
console.log('=== Basic WebSocket ===');
// Note: These examples show patterns - you'll need a real WebSocket server
function basicWebSocket() {
// Create connection
const socket = new WebSocket('wss://echo.websocket.org');
socket.addEventListener('open', (event) => {
console.log('Connected to server');
socket.send('Hello Server!');
});
socket.addEventListener('message', (event) => {
console.log('Received:', event.data);
});
socket.addEventListener('error', (error) => {
console.error('WebSocket error:', error);
});
socket.addEventListener('close', (event) => {
console.log(`Closed: ${event.code} - ${event.reason}`);
});
return socket;
}
// ============================================
// WEBSOCKET STATES
// ============================================
console.log('\n=== WebSocket States ===');
function checkState(socket) {
const states = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];
console.log('State:', states[socket.readyState]);
return socket.readyState === WebSocket.OPEN;
}
// ============================================
// WEBSOCKET CLIENT CLASS
// ============================================
console.log('\n=== WebSocket Client Class ===');
class WebSocketClient {
constructor(url, options = {}) {
this.url = url;
this.options = {
reconnect: true,
reconnectInterval: 1000,
maxReconnectAttempts: 5,
...options,
};
this.socket = null;
this.reconnectAttempts = 0;
this.listeners = new Map();
this.messageQueue = [];
}
connect() {
return new Promise((resolve, reject) => {
console.log('Connecting to:', this.url);
this.socket = new WebSocket(this.url);
this.socket.onopen = () => {
console.log('Connected!');
this.reconnectAttempts = 0;
this.flushQueue();
this.emit('connect');
resolve(this);
};
this.socket.onclose = (event) => {
console.log('Disconnected:', event.code);
this.emit('disconnect', event);
if (this.options.reconnect && !event.wasClean) {
this.attemptReconnect();
}
};
this.socket.onerror = (error) => {
console.error('Error:', error);
this.emit('error', error);
reject(error);
};
this.socket.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
this.emit('message', data);
if (data.type) {
this.emit(data.type, data.payload);
}
} catch (e) {
this.emit('message', event.data);
}
};
});
}
send(data) {
const message = typeof data === 'string' ? data : JSON.stringify(data);
if (this.isConnected()) {
this.socket.send(message);
} else {
console.log('Queuing message');
this.messageQueue.push(message);
}
}
flushQueue() {
console.log(`Flushing ${this.messageQueue.length} queued messages`);
while (this.messageQueue.length > 0) {
this.socket.send(this.messageQueue.shift());
}
}
isConnected() {
return this.socket && this.socket.readyState === WebSocket.OPEN;
}
attemptReconnect() {
if (this.reconnectAttempts >= this.options.maxReconnectAttempts) {
console.log('Max reconnection attempts reached');
this.emit('reconnectFailed');
return;
}
this.reconnectAttempts++;
const delay = this.options.reconnectInterval * this.reconnectAttempts;
console.log(
`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`
);
setTimeout(() => {
this.connect().catch(() => {});
}, delay);
}
on(event, callback) {
if (!this.listeners.has(event)) {
this.listeners.set(event, []);
}
this.listeners.get(event).push(callback);
return this;
}
off(event, callback) {
if (this.listeners.has(event)) {
const callbacks = this.listeners.get(event);
const index = callbacks.indexOf(callback);
if (index !== -1) callbacks.splice(index, 1);
}
return this;
}
emit(event, data) {
if (this.listeners.has(event)) {
this.listeners.get(event).forEach((cb) => cb(data));
}
}
close(code = 1000, reason = '') {
this.options.reconnect = false;
if (this.socket) {
this.socket.close(code, reason);
}
}
}
// Demo usage (without actual connection)
console.log('Created WebSocketClient class');
// ============================================
// HEARTBEAT PATTERN
// ============================================
console.log('\n=== Heartbeat Pattern ===');
class WebSocketWithHeartbeat extends WebSocketClient {
constructor(url, options = {}) {
super(url, { heartbeatInterval: 30000, ...options });
this.heartbeatTimer = null;
this.lastPong = Date.now();
}
connect() {
return super.connect().then((client) => {
this.startHeartbeat();
return client;
});
}
startHeartbeat() {
this.heartbeatTimer = setInterval(() => {
if (this.isConnected()) {
// Check if we received pong recently
if (Date.now() - this.lastPong > this.options.heartbeatInterval * 2) {
console.log('Connection appears dead, reconnecting...');
this.socket.close();
return;
}
this.send({ type: 'ping', timestamp: Date.now() });
}
}, this.options.heartbeatInterval);
this.on('pong', (data) => {
this.lastPong = Date.now();
const latency = Date.now() - data.timestamp;
console.log(`Heartbeat: ${latency}ms latency`);
});
}
close(code, reason) {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
}
super.close(code, reason);
}
}
console.log('Created WebSocketWithHeartbeat class');
// ============================================
// REQUEST-RESPONSE PATTERN
// ============================================
console.log('\n=== Request-Response Pattern ===');
class WebSocketRPC extends WebSocketClient {
constructor(url, options = {}) {
super(url, options);
this.pendingRequests = new Map();
this.requestId = 0;
this.on('response', (data) => {
const { id, result, error } = data;
const pending = this.pendingRequests.get(id);
if (pending) {
this.pendingRequests.delete(id);
if (error) {
pending.reject(new Error(error));
} else {
pending.resolve(result);
}
}
});
}
async request(method, params, timeout = 5000) {
return new Promise((resolve, reject) => {
const id = ++this.requestId;
const timer = setTimeout(() => {
this.pendingRequests.delete(id);
reject(new Error('Request timeout'));
}, timeout);
this.pendingRequests.set(id, {
resolve: (result) => {
clearTimeout(timer);
resolve(result);
},
reject: (error) => {
clearTimeout(timer);
reject(error);
},
});
this.send({ type: 'request', id, method, params });
});
}
}
console.log('Created WebSocketRPC class');
// ============================================
// CHAT CLIENT
// ============================================
console.log('\n=== Chat Client ===');
class ChatClient extends WebSocketClient {
constructor(url) {
super(url);
this.currentRoom = null;
this.username = null;
}
setUsername(username) {
this.username = username;
this.send({ type: 'setUsername', username });
}
join(room) {
this.currentRoom = room;
this.send({ type: 'join', room });
console.log(`Joined room: ${room}`);
}
leave() {
if (this.currentRoom) {
this.send({ type: 'leave', room: this.currentRoom });
console.log(`Left room: ${this.currentRoom}`);
this.currentRoom = null;
}
}
sendMessage(text) {
if (this.currentRoom) {
this.send({
type: 'chat',
room: this.currentRoom,
text,
timestamp: Date.now(),
});
} else {
console.warn('Not in a room');
}
}
sendTyping(isTyping) {
if (this.currentRoom) {
this.send({
type: 'typing',
room: this.currentRoom,
isTyping,
});
}
}
}
console.log('Created ChatClient class');
// ============================================
// LIVE DATA FEED
// ============================================
console.log('\n=== Live Data Feed ===');
class LiveFeed extends WebSocketClient {
constructor(url) {
super(url);
this.subscriptions = new Set();
}
subscribe(channel) {
this.subscriptions.add(channel);
if (this.isConnected()) {
this.send({ type: 'subscribe', channel });
}
console.log(`Subscribed to: ${channel}`);
}
unsubscribe(channel) {
this.subscriptions.delete(channel);
if (this.isConnected()) {
this.send({ type: 'unsubscribe', channel });
}
console.log(`Unsubscribed from: ${channel}`);
}
connect() {
return super.connect().then((client) => {
// Resubscribe on reconnect
this.subscriptions.forEach((channel) => {
this.send({ type: 'subscribe', channel });
});
return client;
});
}
}
console.log('Created LiveFeed class');
// ============================================
// MESSAGE BUFFER
// ============================================
console.log('\n=== Message Buffer ===');
class BufferedWebSocket extends WebSocketClient {
constructor(url, options = {}) {
super(url, {
bufferSize: 100,
flushInterval: 100,
...options,
});
this.buffer = [];
this.flushTimer = null;
}
connect() {
return super.connect().then((client) => {
this.startBufferFlush();
return client;
});
}
startBufferFlush() {
this.flushTimer = setInterval(() => {
if (this.buffer.length > 0 && this.isConnected()) {
const batch = this.buffer.splice(0, this.options.bufferSize);
this.socket.send(JSON.stringify({ type: 'batch', messages: batch }));
}
}, this.options.flushInterval);
}
sendBuffered(data) {
this.buffer.push(data);
// Immediate flush if buffer is full
if (this.buffer.length >= this.options.bufferSize) {
this.flushBuffer();
}
}
flushBuffer() {
if (this.buffer.length > 0 && this.isConnected()) {
const batch = this.buffer.splice(0);
this.socket.send(JSON.stringify({ type: 'batch', messages: batch }));
}
}
close(code, reason) {
if (this.flushTimer) {
clearInterval(this.flushTimer);
}
this.flushBuffer(); // Flush remaining on close
super.close(code, reason);
}
}
console.log('Created BufferedWebSocket class');
// ============================================
// SIMULATION WITHOUT SERVER
// ============================================
console.log('\n=== Simulated WebSocket Demo ===');
// Mock WebSocket for demonstration
class MockWebSocket {
constructor() {
this.listeners = {};
this.readyState = 0;
setTimeout(() => {
this.readyState = 1;
this.trigger('open', {});
}, 100);
}
addEventListener(event, callback) {
if (!this.listeners[event]) this.listeners[event] = [];
this.listeners[event].push(callback);
}
trigger(event, data) {
if (this.listeners[event]) {
this.listeners[event].forEach((cb) => cb(data));
}
}
send(data) {
console.log('Sent:', data);
// Echo back for demo
setTimeout(() => {
this.trigger('message', { data });
}, 50);
}
close() {
this.readyState = 3;
this.trigger('close', { code: 1000, wasClean: true });
}
}
// Demo
const mockSocket = new MockWebSocket();
mockSocket.addEventListener('open', () => console.log('Mock connected'));
mockSocket.addEventListener('message', (e) =>
console.log('Mock received:', e.data)
);
setTimeout(() => {
mockSocket.send(JSON.stringify({ type: 'test', message: 'Hello' }));
}, 200);
setTimeout(() => {
mockSocket.close();
}, 400);
// ============================================
// EXPONENTIAL BACKOFF
// ============================================
console.log('\n=== Exponential Backoff ===');
function calculateBackoff(attempt, baseDelay = 1000, maxDelay = 30000) {
const delay = Math.min(baseDelay * Math.pow(2, attempt), maxDelay);
// Add jitter
const jitter = delay * 0.2 * Math.random();
return Math.floor(delay + jitter);
}
for (let i = 0; i < 6; i++) {
console.log(`Attempt ${i}: ${calculateBackoff(i)}ms`);
}
console.log('\n=== Examples Complete ===');