web-dev-qa-db-ja.com

WebSocketのreadyStateが変化するのを待つ方法

ポーリングにフォールバックするWebSocketを実装しようとしています。 WebSocket接続が成功した場合、readyStateは1になりますが、失敗した場合、readyStateは3になり、ポーリングを開始する必要があります。

私は次のようなものを試しました:

var socket = new WebSocket(url);
socket.onmessage = onmsg;
while (socket.readyState == 0)
{
}
if (socket.readyState != 1)
{
    // fall back to polling
    setInterval(poll, interval);
}

私は期待していました socket.readyState非同期に更新し、すぐに読み取れるようにします。ただし、これを実行すると、ブラウザーがフリーズします(あきらめるまで約30分間開いたままにしました)。

おそらくonreadyStateChangedイベントがあると思っていましたが、MDNリファレンスにはありませんでした。

これをどのように実装すればよいですか?どうやら空のループは機能せず、これに対するイベントはありません。

32
Kendall Frey

これはシンプルで完璧に機能します...最大時間に関する条件、またはより堅牢にするための試行回数を追加できます...

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...
}
31
user3215378

以下に、より詳細な説明を示します。すべてのブラウザーが最新のRFCに対応しているわけではないため、まず特定のブラウザーAPIを確認してください。あなたは相談することができます

常に実行状態をチェックするためにループを実行したくないので、余分なオーバーヘッドが必要です。より良い方法は、準備完了状態の変更に関連するすべてのイベントを理解し、それらを適切に結び付けることです。それらは次のとおりです。

onclose WebSocket接続のreadyStateがCLOSEDに変更されたときに呼び出されるイベントリスナー。リスナーは、「close」という名前のCloseEventを受け取ります。

onerrorエラーが発生したときに呼び出されるイベントリスナー。これは、「エラー」という名前の単純なイベントです。

onmessageサーバーからメッセージを受信したときに呼び出されるイベントリスナー。リスナーは、「message」という名前のMessageEventを受け取ります。

onopen WebSocket接続のreadyStateがOPENに変更されたときに呼び出されるイベントリスナー。これは、接続がデータを送受信する準備ができていることを示します。イベントは、「open」という名前の単純なものです。

JSは完全にイベント駆動型であるため、これらすべてのイベントを接続し、readystateを確認するだけで、WSからポーリングに切り替えることができます。

Mozillaのリファレンスをご覧になることをお勧めします。RFC文書より読みやすく、APIの概要とその仕組みがわかります。 https://developer.mozilla.org/en- US/docs/WebSockets/WebSockets_reference/WebSocket

失敗した場合は、再試行のためのコールバックを行うことを忘れないでください。再接続が成功するためのコールバックが発生するまでポーリングしてください。

24
Axel

http://dev.w3.org/html5/websockets/ を見てください

「イベントハンドラ」を検索し、テーブルを見つけます。

onopen-> open
onmessage-> message
onerror-> error
onclose-> close

function update(e){ /*Do Something*/};
var ws = new WebSocket("ws://localhost:9999/");

ws.onmessage = update;
6

プーリングはまったく使用していません。代わりに、キューを使用します。まず、新しい送信関数とキューを作成します。

var msgs = []
function send (msg) {
  if (ws.readyState !== 1) {
    msgs.Push(msg)
  } else {
    ws.send(msg)
  }
}

次に、接続が最初に確立されたときに読み取りと送信を行う必要があります。

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接続は、最初のクリックでのみ作成されます。通常、2回目のメッセージは直接送信され始めます。

2
Jan Topiński

私のユースケースでは、接続が失敗した場合に画面にエラーを表示したかった。

let $connectionError = document.getElementById("connection-error");

setTimeout( () => {
  if (ws.readyState !== 1) {
    $connectionError.classList.add( "show" );
  }
}, 100 );  // ms

Safari(9.1.2)ではerrorイベントは発生しないことに注意してください-そうでなければ、これをエラーハンドラーに配置します。

0
akauppi

Whileループがスレッドをロックしている可能性があります。使用してみてください:

setTimeout(function(){
    if(socket.readyState === 0) {
        //do nothing
    } else if (socket.readyState !=1) {
        //fallback
        setInterval(poll, interval);
    }
}, 50);
0
agryson