Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript: Properly Globalizing a Websocket Send Function

I am working with a WebSocket and trying to be able to send socket data at anytime from throughout my application. When I attempt to access the send command from within another function, I am receiving:

Uncaught InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable.

This only is occuring when I call a function, this is how I am setting up my websocket:

Main.socket = (function() {
var socket = new WebSocket("ws://server:port");

socket.onopen = function() {  
    console.log("Socket has been opened!");  
}

function send() {
    socket.send('test');
}

return {
    socket: socket,
    send: send
}
})();

I am able to call the function globally, and also when I console.log Main.socket from within a function it is able to see the socket. But when I call the send function I get that error.

like image 515
smcgovern Avatar asked Oct 25 '13 18:10

smcgovern


Video Answer


2 Answers

Here is an alternative solution to waiting for the web socket connection to come online, replace your call to :

function send() {
    web_socket.send('test');
}

with this :

function send(msg) {

    wait_for_socket_connection(socket, function() {
        socket.send(msg);
    });
};

function wait_for_socket_connection(socket, callback){

    setTimeout(
        function(){
            if (socket.readyState === 1) {
                if(callback !== undefined){
                    callback();
                }
                return;

            } else {

                console.log("... waiting for web socket connection to come online");

                wait_for_socket_connection(socket,callback);
            }
        }, 5);
};
like image 189
Scott Stensland Avatar answered Sep 28 '22 07:09

Scott Stensland


The problem is that the socket has not been opened yet. WebSocket.send cannot be used until the asynchronous onopen event occurs.

While using setTimeout (for a long enough duration) "should work", the correct way to deal with asynchronous JavaScript programming is to treat program flow as a sequence of dependent events.

In any case, here is a small example showing how to use a jQuery Deferred Object which (as of jQuery 1.8 isn't broken and honors the Promises/A contract):

Main.socket = (function($) {
   var socket = new WebSocket("ws://server:port");
   // Promise will be called with one argument, the "send" function for this
   // socket.
   var readyPromise = $.Deferred();
   socket.onopen = function() {
     console.log("Socket has been opened!");
     readyPromise.resolve(socket.send)
  }
  return readyPromise;
})(jQuery);

Then later, in the code that uses this little module:

Main.socket.then(function (send) {
   // This will only be called after `Promise.resolve` is called in the module
   // which will be called in the `WebSocket.onopen` callback.
   send("Hello world!");
})
// This code may or may not execute before the `then` function above
// depending upon the state the Promise/Deferred Object.
// However, we can get consistent program flow by using `then`-chaining
// of promises.

Of course you don't have to use Promises - callbacks will work just fine, although I prefer the unified contract/framework of Promises - and you can use whatever names or structure is most fitting.

Also, note that it might not be good to have a single WebSocket for the entire page lifecycle as this won't correctly handle disconnect and recovery scenarios.

like image 40
user2864740 Avatar answered Sep 28 '22 08:09

user2864740