问题
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.
回答1:
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).
回答2:
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.
回答3:
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.
回答4:
@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 )
回答5:
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).
回答6:
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)
回答7:
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 );
},
来源:https://stackoverflow.com/questions/395506/implementing-a-self-resetting-xmlhttprequest-object