WebSocket, even after firing onopen event, still in CONNECTING state

Viewed 3978

WebSocket send method throws exception even on trying after onconnect. Not always, but many times I see the logged exceptions in console.

This is the typescript code:-

  open() {
    if ('WebSocket' in window) {
      this.ws = new WebSocket(ApiUrls.Socket(''));
      this.ws.onopen = () => {
        const authentication = {
          msgType : 'Authenticate',
          data : {
            token : localStorage.getItem('authToken')
          }
        }
        this.ws.send(JSON.stringify(authentication));
      };

      this.ws.onmessage = (evt) => {
        // console.log(evt);
        this.parseMessage(evt.data);
      };

      this.ws.onclose = () => {
        this.connected = false;
        // console.log('retrying connection with server');
        if (localStorage.getItem('authToken')) {
          this.reconnectTimeout = setTimeout(this.open.bind(this), 1000);
        }
      };
    } else {
      alert('WebSocket NOT supported by your Browser!');
    }
  }

Exception in console:-

ERROR DOMException: Failed to execute 'send' on 'WebSocket': Still in CONNECTING state.
    at WebSocket.ws.onopen [as __zone_symbol__ON_PROPERTYopen] (http://localhost:4200/main.bundle.js:5586:26)
    at WebSocket.wrapFn (http://localhost:4200/polyfills.bundle.js:3711:39)
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask (http://localhost:4200/polyfills.bundle.js:2970:31)
    at Object.onInvokeTask (http://localhost:4200/vendor.bundle.js:87677:33)
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask (http://localhost:4200/polyfills.bundle.js:2969:36)
    at Zone.webpackJsonp.../../../../zone.js/dist/zone.js.Zone.runTask (http://localhost:4200/polyfills.bundle.js:2737:47)
    at ZoneTask.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneTask.invokeTask [as invoke] (http://localhost:4200/polyfills.bundle.js:3044:34)
    at invokeTask (http://localhost:4200/polyfills.bundle.js:4085:14)
    at WebSocket.globalZoneAwareCallback (http://localhost:4200/polyfills.bundle.js:4111:17)

It's too verbose, but concerned message is, ERROR DOMException: Failed to execute 'send' on 'WebSocket': Still in CONNECTING state. at WebSocket.ws.onopen [as __zone_symbol__ON_PROPERTYopen].

2 Answers

This breaks when the open method is called twice. In that case the ws property is overwritten with a new Websocket that is still in connecting state. The wrong WebSocket is then used in the onopen handler (which is async).

const local_websocket = new WebSocket(ApiUrls.Socket(''));
this.ws = local_websocket;
this.ws.onopen = () => {
    const authentication = {
        msgType : 'Authenticate',
        data : {
            token : localStorage.getItem('authToken')
        }
    }
    local_websocket.send(JSON.stringify(authentication));
}; 

I have found that this seems to help:

this.ws.onopen = (e) => {
  // Not sure if the following line is necessary but doesn't hurt
  if (e.target.readyState !== WebSocket.OPEN) return;
  const authentication = {
    msgType : 'Authenticate',
    data : {
      token : localStorage.getItem('authToken')
    }
  }

  // Here's the key: use the websocket instance that triggered the onopen event
  e.target.send(JSON.stringify(authentication));
}

The difference is that you use the websocket instance that triggered the onopen event instead of the one that is living in your this.ws variable, since this.ws could have changed in the meantime.

Related