最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Javascript: Properly Globalizing a Websocket Send Function - Stack Overflow

programmeradmin2浏览0评论

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 mand 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.

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 mand 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.

Share Improve this question edited Oct 25, 2013 at 18:48 smcgovern asked Oct 25, 2013 at 18:42 smcgovernsmcgovern 921 silver badge8 bronze badges 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]." – user2864740 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 e 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. – smcgovern 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. – user2864740 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! – smcgovern 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. – user2864740 Commented Oct 25, 2013 at 19:10
Add a ment  | 

2 Answers 2

Reset to default 4

Here is an alternative solution to waiting for the web socket connection to e 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 e online");

                wait_for_socket_connection(socket,callback);
            }
        }, 5);
};

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.

发布评论

评论列表(0)

  1. 暂无评论