webscoket-mitt
Version:
A plugin library that implements native WebSocket
2 lines (1 loc) • 5.43 kB
JavaScript
"use strict";const e=require("mitt")();class t{static MessageType={Beat:"beat",Message:"message",WS_CONNECT:"ws:connect",WS_ERROR:"ws:error",WS_CLOSE:"ws:close",WS_EXIT:"ws:exit",WS_RECONNECT:"ws:reconnect",WS_RECONNECT_FAILED:"ws:reconnect-failed",WS_RECONNECT_SUCCESS:"ws:reconnect-success",WS_HEARBEAT_FAILED:"ws:heartbeat-failed"};static CreateLifecycleMessage(e,t){return{id:"ws_msg_id_"+Date.now(),event:e,data:t,timestamp:Date.now()}}static CreateMessage(e,t){return JSON.stringify({id:"ws_msg_id_"+Date.now(),event:e,data:t,timestamp:Date.now()})}static ParseMessage(e){try{return JSON.parse(e)}catch(t){return console.error("消息解析失败:",t,e),null}}}class s{ws=null;url;heartbeatInterval;heartbeatTimer=null;heartbeatMissCount=0;heartbeatMaxMissCount;reconnectTimer=null;reconnectDelay;maxReconnectDelay;reconnectCount=0;maxReconnectCount;sendQueue=[];maxQueueSize=1e3;manuallyClosed=!1;logging=!1;DEFAULT_OPTIONS={url:"",heartbeatInterval:3e4,heartbeatMaxMissCount:4,reconnectDelay:1e3,maxReconnectDelay:1e4,maxReconnectCount:5,logging:!1};static MessageType=t.MessageType;constructor(e){const t=Object.assign({},this.DEFAULT_OPTIONS,e);this.url=t.url,this.heartbeatInterval=t.heartbeatInterval,this.heartbeatMaxMissCount=t.heartbeatMaxMissCount,this.reconnectDelay=t.reconnectDelay,this.maxReconnectDelay=t.maxReconnectDelay,this.maxReconnectCount=t.maxReconnectCount,this.logging=t.logging}setOptions(e){const t=Object.assign({},this.DEFAULT_OPTIONS,e);this.url=t.url,this.heartbeatInterval=t.heartbeatInterval,this.heartbeatMaxMissCount=t.heartbeatMaxMissCount,this.reconnectDelay=t.reconnectDelay,this.maxReconnectDelay=t.maxReconnectDelay,this.maxReconnectCount=t.maxReconnectCount,this.logging=t.logging}connect(){if(!this.url)throw new Error("未设置服务地址");if(!this.ws||this.ws.readyState!==WebSocket.OPEN){this.manuallyClosed=!1;try{this.ws=new WebSocket(this.url)}catch(e){return console.error("[websocket-mitt]: ws连接失败:",e),void this.reconnect()}this.ws.onopen=()=>{if(this.flushSendQueue(),this.startHeartbeat(),this.reconnectCount>0){this.logging&&console.log("[websocket-mitt]: ws重连成功!");const s=t.CreateLifecycleMessage(t.MessageType.WS_RECONNECT_SUCCESS,"");e.emit(t.MessageType.WS_RECONNECT_SUCCESS,s)}else{this.logging&&console.log("[websocket-mitt]: ws连接成功!");const s=t.CreateLifecycleMessage(t.MessageType.WS_CONNECT,"");e.emit(t.MessageType.WS_CONNECT,s)}this.reconnectDelay=1e3,this.reconnectCount=0},this.ws.onmessage=s=>{const n=t.ParseMessage(s.data);n&&(this.logging&&console.log("[websocket-mitt]: ws接收到消息:",n),this.heartbeatMissCount=0,e.emit(t.MessageType.Message,n),n.event!==t.MessageType.Message&&e.emit(n.event,n))},this.ws.onclose=s=>{this.logging&&console.log("[websocket-mitt]: ws关闭连接,",s);const n=t.CreateLifecycleMessage(t.MessageType.WS_CLOSE,s);e.emit(t.MessageType.WS_CLOSE,n),this.stopHeartbeat(),this.reconnect()},this.ws.onerror=s=>{console.error("[websocket-mitt]: ws发生错误,",s);const n=t.CreateLifecycleMessage(t.MessageType.WS_ERROR,s);e.emit(t.MessageType.WS_ERROR,n)}}}reconnect(){if(this.manuallyClosed)return;if(this.reconnectCount>=this.maxReconnectCount){console.error(`[websocket-mitt] 已达到最大重连次数(${this.maxReconnectCount}),停止重连`);const s=t.CreateLifecycleMessage(t.MessageType.WS_RECONNECT_FAILED,"");return e.emit(t.MessageType.WS_RECONNECT_FAILED,s),void this.exit()}if(this.reconnectTimer)return;const s=Math.min(this.reconnectDelay,this.maxReconnectDelay);this.reconnectTimer=setTimeout((()=>{this.logging&&console.log(`[websocket-mitt] 开始进行 ${this.reconnectCount+1} 次重连`);const s=t.CreateLifecycleMessage(t.MessageType.WS_RECONNECT,this.reconnectCount+1);e.emit(t.MessageType.WS_RECONNECT,s),this.ws=null,this.connect(),this.reconnectTimer=null,this.reconnectDelay=Math.min(2*this.reconnectDelay,this.maxReconnectDelay),this.reconnectCount++}),s)}exit(){this.manuallyClosed=!0,this.stopHeartbeat(),this.sendQueue=[],this.ws&&(this.ws.close(1e3,"client close"),this.ws=null),this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.logging&&console.log("[websocket-mitt] ws服务关闭!");const s=t.CreateLifecycleMessage(t.MessageType.WS_EXIT,"");e.emit(t.MessageType.WS_EXIT,s)}startHeartbeat(){this.stopHeartbeat(),this.heartbeatTimer=setInterval((()=>{if(this.ws?.readyState!==WebSocket.OPEN)return;if(this.heartbeatMissCount>=this.heartbeatMaxMissCount){console.warn("心跳检测连续失败,准备重连..."),this.stopHeartbeat();const s=t.CreateLifecycleMessage(t.MessageType.WS_HEARBEAT_FAILED,"");return e.emit(t.MessageType.WS_HEARBEAT_FAILED,s),void this.ws?.close()}const s=t.CreateMessage(t.MessageType.Beat,"");this.wsSendMessage(s),this.heartbeatMissCount++}),this.heartbeatInterval)}stopHeartbeat(){this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null),this.heartbeatMissCount=0}$on(t,s){e.on(t,s)}$emit(e,s){const n=t.CreateMessage(e,s);this.wsSendMessage(n)}wsSendMessage(e){if(this.ws&&this.ws.readyState===WebSocket.OPEN)try{this.ws.send(e)}catch(t){console.error("发送消息失败:",t),this.sendQueue.push(e)}else this.sendQueue.length>=this.maxQueueSize&&this.sendQueue.shift(),this.sendQueue.push(e)}flushSendQueue(){for(;this.ws&&this.ws.readyState===WebSocket.OPEN&&this.sendQueue.length>0;){const e=this.sendQueue.shift();e&&this.wsSendMessage(e)}}}module.exports=s;