views:

1224

answers:

6

I'm trying to implement a comet style, long polling connection using an XMLHttpResponse object. The idea is to maintain an open connection to a server which sends data when it is available (faking push). As soon as the XHR object completes, I need to spawn a new one to wait for any fresh data.

Below is a snippet of code which outlines a solution that works but, as the comment says, only because of a timeout which I need to get rid of.

window.onload = function(){
    XHR.init();
}


XHR = {
    init: function() {
        this.xhr = new XMLHttpRequest();
        this.xhr.open( "GET", "proxy.php?salt="+Math.round( Math.random()*10000 ), true );
        this.xhr.onreadystatechange = this.process.bind( this );
        this.xhr.send( null );
    },
    process: function() {
        if( this.xhr.readyState == 4 ) {

            // firebug console
            console.log( this.xhr.responseText );

            // ** Attempting to create new XMLHttpRequest object to
            // replace the one that's just completed
            // doesn't work without the timeout
            setTimeout( function() { this.init() }.bind( this ), 1000 );
        }
    }
}


Function.prototype.bind = function( obj ) {
    var method = this;
    return function() {
        return method.apply( obj, arguments );
    }
}


// proxy.php - a dummy that keeps the XHR object waiting for a response
<?php
$rand = mt_rand( 1, 10 );
sleep( $rand );
echo date( 'H:i:s' ).' - '.$rand;

I think the problem might be that you can't delete an object (xhr) from it's own event handler (process) as is the case here. especially because the 'this' within the handler is bound to an object (XHR) which contains the object (xhr) I'm trying to delete. Kinda circular!

Can anyone help? The above example is the closest I can get.

A: 

What you're doing is effectively polling, why make it more complex than it needs to be, and just poll every few seconds? Or every second, how much time are you really saving and is it really that important, and you're going to be tying up an awful lot of sockets on the server end if you have a lot of users.

stu
A: 

But to actually try and answer your question, the way to delete things that aren't this is to set a timer to call a function that does the deleting, that way, it is not itself that is being deleted.

stu
A: 

@stu

In this app response time is key - actually what's important is that all clients are updated simultaneously (or as close as possible)

the number off connections will be fairly limited ~50max and the interval between changes could be several minutes.

If polling is used it would need to be very short ~100ms which would lead to an awful lot of unnecessary requests (which will be expensive for the little php socket server I've cobbled together - I know, I know python would be better for the server but I don't know it well enough )

meouw
erm, it's hard for me to get the idea of the scale you're talking about. I think enterprise/worstcase in general, and to me, python isn't any better than php or anything else. The problem is that the webserver is goin to be forking a process, you probably want to avoid that.
stu
A: 

You probably shouldn't use XMLHTTPRequest at all for this.

Several years ago, long before XMLHTTPRequest was known, I created a chat program to be used in a normal browser. The window with the chat was in a frame where the data come from a cgi-script that never ended. Whenever there was new data, I just sent it and it was immediately displayed at the client.

I guess you can use something similar today:

  • Create a function in your page that will do the update.
  • Create an IFRAME, you can make it invisible if you want
  • Set the source of the IFRAME to your script that generates the data
  • The data could be encapsulated in SCRIPT-tags. If I remember correctly browsers require to have the whole content of the script tag before trying to evaluate it. Call your update function:

    <script type="text/javascript">myupdate("mydata");</script>

  • Even if you haven't something to send, do send a space every 5 second or so to reset the timeout at the browser.
  • If I remember correctly you had to send about 1K data at the start of the transaction to fill the prefetch buffer at the browser (maybe this have to be increased in browsers of today)
some
A: 

You might have an easier time implementing reuse, adding in the abort() method:

XHR = {
    init: function() {
        if (!this.xhr) { // setup only once
            this.xhr = new XMLHttpRequest();
            this.xhr.onreadystatechange = this.process.bind( this );
        }
        this.xhr.abort(); // reset xhr
        this.xhr.open(/*...*/);
        this.xhr.send(null);
    },
    process: function() {
        if( this.xhr.readyState == 4 ) {

            // firebug console
            console.log( this.xhr.responseText );

            // start next round
            this.init();
        }
    }
};


@meouw [comment]

If you're getting the same result, than I'd guess you're have a cache issue (that Math.random() isn't solving) or you're not marking what's been sent over previous requests (resending the same data each time).

Jonathan Lonowski
This solution has the same problem as the original. The XHR handler immediately and repeatedly returns with the last result.I think this is because the old or unrefreshed object is still hanging around
meouw
A: 

Just use jquery and do something like this:

  function getData() {
    $.getJSON(someUrl, gotData);
  }

  // Whenever a query stops, start a new one.
  $(document).ajaxStop(getData, 0);
  // Start the first query.
  getData();

My slosh examples do this (since it's pretty much a comet server).

Dustin
This answers the 'how to do it' question but not the 'how it works' question.I'll have to dig around in the jQuery source to find out. Specifically, how the ajaxStop event is raised.
meouw