web-dev-qa-db-fra.com

Comment attendre qu'un WebSocket readyState change

J'essaie d'implémenter un WebSocket avec un repli sur la scrutation. Si la connexion WebSocket réussit, readyState devient 1, mais si elle échoue, readyState vaut 3 et je devrais commencer à interroger.

J'ai essayé quelque chose comme ça:

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

Je m'attendais à ce que socket.readyState se mette à jour de manière asynchrone et me permette de le lire immédiatement. Cependant, lorsque je lance ceci, mon navigateur se bloque (je l'ai laissé ouvert pendant environ une demi-minute avant d'abandonner).

Je pensais qu'il y avait peut-être un événement onreadyStateChanged, mais je n'en ai pas vu dans la référence MDN.

Comment devrais-je mettre en œuvre cela? Apparemment, une boucle vide ne fonctionnera pas et il n'y a aucun événement pour cela.

29
Kendall Frey

C’est simple et cela fonctionne parfaitement ... vous pouvez ajouter une condition sur le temps maximal ou le nombre d’essais pour le rendre plus robuste ...

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();
                }
                return;

            } else {
                console.log("wait for connection...")
                waitForSocketConnection(socket, callback);
            }

        }, 5); // wait 5 milisecond for the connection...
}
31
user3215378

Voici une explication plus élaborée. Tout d'abord, vérifiez l'API du navigateur spécifique, car tous les navigateurs ne seront pas sur la dernière RFC. Vous pouvez consulter le 

Vous ne voulez pas exécuter une boucle pour vérifier en permanence l'état de lecture, c'est une surcharge supplémentaire dont vous n'avez pas besoin. Une meilleure approche consiste à comprendre tous les événements pertinents pour un changement d'état de lecture, puis à les câbler de manière appropriée. Ils sont comme suit:

onclose Un écouteur d'événement à appeler lorsque l'état ready de la connexion WebSocket devient CLOSED. L'écouteur reçoit un événement CloseEvent nommé "close".

onerror Un écouteur d'événement à appeler en cas d'erreur. C'est un événement simple nommé "erreur".

onmessage Un écouteur d'événements à appeler lorsqu'un message est reçu du serveur. L'écouteur reçoit un MessageEvent nommé "message".

onopen Un écouteur d'événements à appeler lorsque l'état ready de la connexion WebSocket devient OPEN; cela indique que la connexion est prête à envoyer et recevoir des données. L’événement est simple et porte le nom «open».

JS est entièrement piloté par les événements. Vous devez donc simplement câbler tous ces événements et vérifier l'état de lecture pour pouvoir passer en conséquence de WS à une interrogation.

Je vous recommande de consulter la référence Mozilla, elle est plus facile à lire que le document RFC et vous donnera un bon aperçu de l'API et de son fonctionnement: voir https://developer.mozilla.org/en-US/ docs/WebSockets/WebSockets_reference/WebSocket

N'oubliez pas de faire un rappel pour réessayer en cas d'échec et d'interroger jusqu'à ce que le rappel d'une reconnexion réussie soit déclenché.

24
Axel

Regardez sur http://dev.w3.org/html5/websockets/

Recherchez "Gestionnaire d'événements" et recherchez la table.

onopen -> ouvert
onmessage -> message
onerror -> error
onclose -> fermer

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

ws.onmessage = update;
5

Je n'utilise pas du tout la mise en commun. Au lieu de cela, j'utilise la file d'attente . Je crée d'abord une nouvelle fonction d'envoi et une file d'attente:

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

Ensuite, je dois lire et envoyer lorsque la connexion est établie pour la première fois:

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))
}

La connexion WebSocket dans cet exemple est créée uniquement au premier clic. Habituellement, les seconds messages commencent à être envoyés directement. 

2
Jan Topiński

Dans mon cas d'utilisation, je voulais afficher une erreur à l'écran si la connexion échouait.

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

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

Notez que dans Safari (9.1.2) aucun événement error n'est déclenché - sinon, je l'aurais placé dans le gestionnaire d'erreurs.

0
akauppi

Votre boucle while bloque probablement votre fil. Essayez d'utiliser:

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