How to wait for a WebSocket's readyState to change

后端 未结 9 2297
梦如初夏
梦如初夏 2020-11-30 00:54

I\'m trying to implement a WebSocket with a fallback to polling. If the WebSocket connection succeeds, readyState becomes 1, but if it fails, readyState

相关标签:
9条回答
  • 2020-11-30 00:56

    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 error event gets fired - otherwise I would have placed this in the error handler.

    0 讨论(0)
  • 2020-11-30 01:04

    Look on http://dev.w3.org/html5/websockets/

    Search for "Event handler" and find the Table.

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

    function update(e){ /*Do Something*/};
    var ws = new WebSocket("ws://localhost:9999/");
    
    ws.onmessage = update;
    
    0 讨论(0)
  • 2020-11-30 01:07

    If you use async/await and you just want to wait until the connection is available I would suggest this function :

    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 async function) :

    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
    }
    
    0 讨论(0)
  • 2020-11-30 01:08

    tl;dr

    A simple proxy wrapper to add state event to WebSocket which will be emitted when its readyState changes:

    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 Proxy object to monitor inner WebSocket state.

    This is a good article which explains how to do it Debugging WebSockets using JS Proxy Object

    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; 
    
    0 讨论(0)
  • 2020-11-30 01:08

    Your while loop is probably locking up your thread. Try using:

    setTimeout(function(){
        if(socket.readyState === 0) {
            //do nothing
        } else if (socket.readyState !=1) {
            //fallback
            setInterval(poll, interval);
        }
    }, 50);
    
    0 讨论(0)
  • 2020-11-30 01:13

    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.

    0 讨论(0)
提交回复
热议问题