5

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.

5
  • 2
    I suspect it is because the socket has not been opened yet - has onopen been called? "An attempt was made to use an [Socket] object that is not, or is no longer, usable [because it is in an Invalid State]." Commented Oct 25, 2013 at 18:48
  • The socket is opened, if I open my console and do Main.socket.send('test'); I am able to see that come through on my server, but when I try to call from within a function it fails. I have added an init function to my Main socket that I call at the start. Commented Oct 25, 2013 at 19:01
  • 1
    If you open the main console and call Main.socket.send, time has already elapsed - and time enough such that socket likely has had ample time to open (also, importantly, the JavaScript code has been idle such that events have been processed). Try this, from the failing code: call the send in a setTimeout(function () { Main.socket.send("yay") }, 500). Of course this is a silly amount of time to wait, in reality we should only wait until onopen is triggered. Using futures can make this chaining easy, but it will require asynchronous usage in the consumer. Commented Oct 25, 2013 at 19:02
  • Ah! That did the trick, so I guess I'll edit my Main.init to have a delay so the websocket has time to connect. Thanks! Commented Oct 25, 2013 at 19:08
  • No, no, that's just an ugly hack. Utilize the onopen callback and expose your own asynchronous binding. If you are using jQuery 1.8+, $.Defered which creates a Promise/A object is handy. Commented Oct 25, 2013 at 19:10

2 Answers 2

4

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);
};
Sign up to request clarification or add additional context in comments.

Comments

2

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.