Skip to main content

reconnecting-websocket

这个是最近做重构遇到的一个第三方包,是基于 Websocket 对象进一步封装的包,主要是加了重连相关的功能

看了下源码其实不难,代码不超过 400 行,直接开读吧

先看下自定义事件相关的源码:

// ...
var self = this;
var eventTarget = document.createElement("div");
eventTarget.addEventListener("open", function (event) {
self.onopen(event);
});
eventTarget.addEventListener("close", function (event) {
self.onclose(event);
});
eventTarget.addEventListener("connecting", function (event) {
self.onconnecting(event);
});
eventTarget.addEventListener("message", function (event) {
self.onmessage(event);
});
eventTarget.addEventListener("error", function (event) {
self.onerror(event);
});
// Expose the API required by EventTarget
this.addEventListener = eventTarget.addEventListener.bind(eventTarget);
this.removeEventListener = eventTarget.removeEventListener.bind(eventTarget);
this.dispatchEvent = eventTarget.dispatchEvent.bind(eventTarget);
// 创建事件
function generateEvent(s, args) {
// 该特性在后续版本已经废弃,建议使用 CustomEvent 创建自定义事件
var evt = document.createEvent("CustomEvent");
evt.initCustomEvent(s, false, false, args);
return evt;
}
// ...
// 触发事件
eventTarget.dispatchEvent(generateEvent("connecting"));

这里是借助了 DOM 节点继承的 EventTarget 接口来监听五个自定义事件 open/close/connecting/message/error open/close/message/error 比较好理解,跟 websocket 的事件一一对应,在触发 websocket 的事件时加上其他逻辑。connecting 表示的是开始连接到连接建立的中间状态

开始连接时,会执行 this.open

// ...
this.open = function (reconnectAttempt) {
ws = new WebSocket(self.url, protocols || []);
ws.binaryType = this.binaryType;
if (reconnectAttempt) {
if (
// 超过最大重连数,不再尝试重连
this.maxReconnectAttempts &&
this.reconnectAttempts > this.maxReconnectAttempts
) {
return;
}
} else {
eventTarget.dispatchEvent(generateEvent("connecting"));
this.reconnectAttempts = 0;
}
var localWs = ws;
// 超时关闭连接
var timeout = setTimeout(function () {
timedOut = true;
localWs.close();
timedOut = false;
}, self.timeoutInterval);

ws.onopen = function (event) {
clearTimeout(timeout);
self.protocol = ws.protocol;
self.readyState = WebSocket.OPEN;
self.reconnectAttempts = 0;
var e = generateEvent("open");
e.isReconnect = reconnectAttempt;
reconnectAttempt = false;
eventTarget.dispatchEvent(e);
};

ws.onclose = function (event) {
clearTimeout(timeout);
ws = null;
if (forcedClose) {
self.readyState = WebSocket.CLOSED;
eventTarget.dispatchEvent(generateEvent("close"));
} else {
self.readyState = WebSocket.CONNECTING;
var e = generateEvent("connecting");
e.code = event.code;
e.reason = event.reason;
e.wasClean = event.wasClean;
eventTarget.dispatchEvent(e);
if (!reconnectAttempt && !timedOut) {
eventTarget.dispatchEvent(generateEvent("close"));
}
var timeout =
self.reconnectInterval *
Math.pow(self.reconnectDecay, self.reconnectAttempts);
// 失败重连
setTimeout(
function () {
self.reconnectAttempts++;
self.open(true);
},
timeout > self.maxReconnectInterval
? self.maxReconnectInterval
: timeout
);
}
};

ws.onmessage = function (event) {
var e = generateEvent("message");
e.data = event.data;
eventTarget.dispatchEvent(e);
};

ws.onerror = function (event) {
eventTarget.dispatchEvent(generateEvent("error"));
};
};
// ...