Javascript 如何等待 WebSocket 的 readyState 更改
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13546424/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to wait for a WebSocket's readyState to change
提问by Kendall Frey
I'm trying to implement a WebSocket with a fallback to polling. If the WebSocket connection succeeds, readyStatebecomes 1, but if it fails, readyStateis 3, and I should begin polling.
我正在尝试实现一个带有轮询回退的 WebSocket。如果 WebSocket 连接成功,则readyState变为 1,如果失败,readyState则为 3,我应该开始轮询。
I tried something like this:
我试过这样的事情:
var socket = new WebSocket(url);
socket.onmessage = onmsg;
while (socket.readyState == 0)
{
}
if (socket.readyState != 1)
{
// fall back to polling
setInterval(poll, interval);
}
I was expecting socket.readyStateto update asynchronously, and allow me to read it immediately. However, when I run this, my browser freezes (I left it open for about half a minute before giving up).
我期待socket.readyState异步更新,并允许我立即阅读。但是,当我运行它时,我的浏览器冻结了(在放弃之前我将它打开了大约半分钟)。
I thought perhaps there was an onreadyStateChangedevent, but I didn't see one in the MDN reference.
我想也许有一个onreadyStateChanged事件,但我没有在 MDN 参考中看到一个。
How should I be implementing this? Apparently an empty loop won't work, and there is no event for this.
我应该如何实施?显然,空循环不起作用,并且没有事件。
回答by user3215378
This is simple and it work perfectly... you can add condition about maximal time, or number of try to make it more robust...
这很简单,而且效果很好……您可以添加有关最大时间的条件或尝试次数以使其更健壮……
function sendMessage(msg){
// Wait until the state of the socket is not ready and send the message when it is...
waitForSocketConnection(ws, function(){
console.log("message sent!!!");
ws.send(msg);
});
}
// Make the function wait until the connection is made...
function waitForSocketConnection(socket, callback){
setTimeout(
function () {
if (socket.readyState === 1) {
console.log("Connection is made")
if (callback != null){
callback();
}
} else {
console.log("wait for connection...")
waitForSocketConnection(socket, callback);
}
}, 5); // wait 5 milisecond for the connection...
}
回答by Axel
Here is a more elaborate explanation. First off, check the specific browser API, as not all browsers will be on the latest RFC. You can consult the
这里有一个更详细的解释。首先,检查特定的浏览器 API,因为并非所有浏览器都将使用最新的 RFC。您可以咨询
You don't want to run a loop to constantly check the readystate, it's extra overhead you don't need. A better approach is to understand all of the events relevant to a readystate change, and then wire them up appropriately. They are as follows:
您不想运行循环来不断检查就绪状态,这是您不需要的额外开销。更好的方法是了解与就绪状态更改相关的所有事件,然后适当地将它们连接起来。它们如下:
oncloseAn event listener to be called when the WebSocket connection's readyState changes to CLOSED. The listener receives a CloseEvent named "close".
onclose当 WebSocket 连接的 readyState 更改为 CLOSED 时要调用的事件侦听器。侦听器接收名为“close”的 CloseEvent。
onerrorAn event listener to be called when an error occurs. This is a simple event named "error".
onerror发生错误时要调用的事件侦听器。这是一个名为“error”的简单事件。
onmessageAn event listener to be called when a message is received from the server. The listener receives a MessageEvent named "message".
onmessage从服务器接收到消息时要调用的事件侦听器。侦听器接收名为“message”的 MessageEvent。
onopenAn event listener to be called when the WebSocket connection's readyState changes to OPEN; this indicates that the connection is ready to send and receive data. The event is a simple one with the name "open".
onopen当 WebSocket 连接的 readyState 更改为 OPEN 时要调用的事件侦听器;这表明连接已准备好发送和接收数据。该事件是一个名为“open”的简单事件。
JS is entirely event driven, so you need to just wire up all of these events and check for the readystate, this way you can switch from WS to polling accordingly.
JS 完全是事件驱动的,因此您只需连接所有这些事件并检查就绪状态,这样您就可以相应地从 WS 切换到轮询。
I recommend you look at the Mozilla reference, it's easier to read than the RFC document and it will give you a good overview of the API and how it works (link).
我建议您查看 Mozilla 参考,它比 RFC 文档更容易阅读,并且可以很好地概述 API 及其工作原理(链接)。
Don't forget to do a callback for a retry if you have a failure and poll until the callback for a successful reconnect is fired.
如果失败并轮询直到成功重新连接的回调被触发,请不要忘记为重试做回调。
回答by Stefan H?ltker
Look on http://dev.w3.org/html5/websockets/
查看http://dev.w3.org/html5/websockets/
Search for "Event handler" and find the Table.
搜索“事件处理程序”并找到表格。
onopen -> open
onmessage -> message
onerror ->error
onclose ->close
onopen -> 打开
onmessage -> 消息
onerror -> 错误
onclose ->close
function update(e){ /*Do Something*/};
var ws = new WebSocket("ws://localhost:9999/");
ws.onmessage = update;
回答by Jan Topiński
I am not using pooling at all. Instead, I use queuing. First I create new send function and a queue:
我根本没有使用池化。相反,我使用排队。首先我创建新的发送函数和一个队列:
var msgs = []
function send (msg) {
if (ws.readyState !== 1) {
msgs.push(msg)
} else {
ws.send(msg)
}
}
Then I need to read and send when the connection is first established:
然后我需要在第一次建立连接时读取和发送:
function my_element_click () {
if (ws == null){
ws = new WebSocket(websocket_url)
ws.onopen = function () {
while (msgs.length > 0) {
ws.send(msgs.pop())
}
}
ws.onerror = function(error) {
// do sth on error
}
}
msg = {type: 'mymessage', data: my_element.value}
send(JSON.stringify(msg))
}
WebSocket connection in this example is created only on the first click. Usually, on second messages start to be sent directly.
本示例中的 WebSocket 连接仅在第一次单击时创建。通常,第二个消息开始直接发送。
回答by vdegenne
If you use async/awaitand you just want to wait until the connection is available I would suggest this function :
如果您使用async/await并且只想等到连接可用,我建议使用此功能:
async connection (socket, timeout = 10000) {
const isOpened = () => (socket.readyState === WebSocket.OPEN)
if (socket.readyState !== WebSocket.CONNECTING) {
return isOpened()
}
else {
const intrasleep = 100
const ttl = timeout / intrasleep // time to loop
let loop = 0
while (socket.readyState === WebSocket.CONNECTING && loop < ttl) {
await new Promise(resolve => setTimeout(resolve, intrasleep))
loop++
}
return isOpened()
}
}
Usage (in asyncfunction) :
用法(在async函数中):
const websocket = new WebSocket('...')
const opened = await connection(websocket)
if (opened) {
websocket.send('hello')
}
else {
console.log("the socket is closed OR couldn't have the socket in time, program crashed");
return
}
回答by Anatoly
tl;dr
tl;博士
A simple proxywrapper to add stateevent to WebSocketwhich will be emitted when its readyStatechanges:
一个简单的代理包装器,用于state向WebSocket添加事件,该事件将在其readyState更改时发出:
const WebSocketProxy = new Proxy(WebSocket, {
construct: function(target, args) {
// create WebSocket instance
const instance = new target(...args);
//internal function to dispatch 'state' event when readyState changed
function _dispatchStateChangedEvent() {
instance.dispatchEvent(new Event('state'));
if (instance.onstate && typeof instance.onstate === 'function') {
instance.onstate();
}
}
//dispatch event immediately after websocket was initiated
//obviously it will be CONNECTING event
setTimeout(function () {
_dispatchStateChangedEvent();
}, 0);
// WebSocket "onopen" handler
const openHandler = () => {
_dispatchStateChangedEvent();
};
// WebSocket "onclose" handler
const closeHandler = () => {
_dispatchStateChangedEvent();
instance.removeEventListener('open', openHandler);
instance.removeEventListener('close', closeHandler);
};
// add event listeners
instance.addEventListener('open', openHandler);
instance.addEventListener('close', closeHandler);
return instance;
}
});
A long explanation:
一个很长的解释:
You can use a Proxyobject to monitor inner WebSocketstate.
This is a good article which explains how to do it Debugging WebSockets using JS Proxy Object
这是一篇很好的文章,解释了如何使用 JS 代理对象调试 WebSockets
And here is an example of code snippet from the article above in case the site won't be available in the future:
这是上面文章中的代码片段示例,以防将来该站点不可用:
// proxy the window.WebSocket object
var WebSocketProxy = new Proxy(window.WebSocket, {
construct: function(target, args) {
// create WebSocket instance
const instance = new target(...args);
// WebSocket "onopen" handler
const openHandler = (event) => {
console.log('Open', event);
};
// WebSocket "onmessage" handler
const messageHandler = (event) => {
console.log('Message', event);
};
// WebSocket "onclose" handler
const closeHandler = (event) => {
console.log('Close', event);
// remove event listeners
instance.removeEventListener('open', openHandler);
instance.removeEventListener('message', messageHandler);
instance.removeEventListener('close', closeHandler);
};
// add event listeners
instance.addEventListener('open', openHandler);
instance.addEventListener('message', messageHandler);
instance.addEventListener('close', closeHandler);
// proxy the WebSocket.send() function
const sendProxy = new Proxy(instance.send, {
apply: function(target, thisArg, args) {
console.log('Send', args);
target.apply(thisArg, args);
}
});
// replace the native send function with the proxy
instance.send = sendProxy;
// return the WebSocket instance
return instance;
}
});
// replace the native WebSocket with the proxy
window.WebSocket = WebSocketProxy;
回答by akauppi
In my use case, I wanted to show an error on screen if the connection fails.
在我的用例中,如果连接失败,我想在屏幕上显示错误。
let $connectionError = document.getElementById("connection-error");
setTimeout( () => {
if (ws.readyState !== 1) {
$connectionError.classList.add( "show" );
}
}, 100 ); // ms
Note that in Safari (9.1.2) no errorevent gets fired - otherwise I would have placed this in the error handler.
请注意,在 Safari (9.1.2) 中不会error触发任何事件 - 否则我会将其放在错误处理程序中。
回答by agryson
Your while loop is probably locking up your thread. Try using:
您的 while 循环可能正在锁定您的线程。尝试使用:
setTimeout(function(){
if(socket.readyState === 0) {
//do nothing
} else if (socket.readyState !=1) {
//fallback
setInterval(poll, interval);
}
}, 50);
回答by Philipp
Just like you defined an onmessagehandler, you can also define an onerrorhandler. This one will be called when the connection fails.
就像定义onmessage处理程序一样,您也可以定义onerror处理程序。当连接失败时将调用此方法。
var socket = new WebSocket(url);
socket.onmessage = onmsg;
socket.onerror = function(error) {
// connection failed - try polling
}

