meouw
meouw

Reputation: 42140

Implementing a self resetting XMLHttpRequest object

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.

Upvotes: 2

Views: 6286

Answers (7)

pramodshetty001
pramodshetty001

Reputation: 31

Just add xhr = null inside the init function. The xhr will be assigned a new connection, and thus will not take previous HTTPRequest value.

init: function() {
        xhr = null;
        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 );
    },

Upvotes: 0

some
some

Reputation: 49582

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)

Upvotes: 0

Jonathan Lonowski
Jonathan Lonowski

Reputation: 123443

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

Upvotes: 0

Dustin
Dustin

Reputation: 90980

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

Upvotes: 1

meouw
meouw

Reputation: 42140

@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 )

Upvotes: 0

stu
stu

Reputation: 8805

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.

Upvotes: 0

stu
stu

Reputation: 8805

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.

Upvotes: 0

Related Questions