views:

131

answers:

4

Hi,

i use the following code to send an ajax-request every 3 seconds:

var refreshId = setInterval(function() {
    $.ajax({
        async: true,
        url: 'gohere',
        dataType: 'text',
        cache: false,
        timeout: 5000,

        success: function(result, textStatus, XMLHttpRequest) {
            alert('textStatus: ' + textStatus + ",\nXHR.readyState: " + XMLHttpRequest.readyState + ",\nXHT.status: " + XMLHttpRequest.status);
        },

        error: function(XMLHttpRequest, textStatus, errorThrown) {
            alert('textStatus: ' + textStatus + ",\nXHR.readyState: " + XMLHttpRequest.readyState + ",\nXHT.status: " + XMLHttpRequest.status);
        }
    });
}, 3000);

The problem is, that even if the home-server is not accessible (eg not connected to the LAN) the success-function will be called instead of the error-function with a "timeout"-status (tested on Ubuntu 10.04 with FireFox 3.6.7).
(Without periodic requests it will work fine: On timeouts the error-function is called with the correct 'timeout'-statusText.)

The success-alert-function will show the following text on timeouts:

    textStatus: success,
    XHR.readyState: 4,
    XHR.status: 0
So i need to use the XHR.status-value, which should be 200 on success, to determine errors. Is this the normal behaviour or did i something wrong?

A: 

Well, messing around with asyncronous function callbacks can get messy :). setInterval() is one of those, so is .ajax().

You should try to use setTimeout() and just fire the next .ajax() call when the last has completed.

(function repeat(){
   $.ajax({
    async: true,
    url: 'gohere',
    dataType: 'text',
    cache: false,
    timeout: 5000,

    success: function(result, textStatus, XMLHttpRequest) {
        alert('textStatus: ' + textStatus + ",\nXHR.readyState: " + XMLHttpRequest.readyState + ",\nXHT.status: " + XMLHttpRequest.status);
    },

    error: function(XMLHttpRequest, textStatus, errorThrown) {
        alert('textStatus: ' + textStatus + ",\nXHR.readyState: " + XMLHttpRequest.readyState + ",\nXHT.status: " + XMLHttpRequest.status);
    },

    complete: function(XMLHttpRequest){
       setTimeout(repeat, 3000);
    }
  });       
}());

If you are insistent to have an .ajax() as close as possible to 3 seconds, you can abort your last (possibly running) ajax request before fireing the new one.

Therefore, assign a variable to the return of .ajax() like

var currentxhr = $.ajax({});

and add a beforeSend callback into your .ajax() call. Within that handler call

currentxhr.abort();

That will abort a possibly running request.

jAndy
Thanks for your reply. This works fine. But now i have the same problem with an `$.each(...)` instead of the `setInterval`. I use it to load multiple images. Do you have a workaround for it?
Biggie
The same problem sometimes occurrs when using `.ajax()` in parallel to the periodic request. Is there a way to avoid checking `XHR.status==0` in the success-function?
Biggie
A: 

Instead of looking at textStatus use this test: xhr.statusText.toUpperCase() === 'OK'

I ran into a problem with FF 3.6.8 having a xhr.status === 0 on 301 pages. I added the above to my jQuery 1.4.2 in the httpSuccess function:

return !xhr.status && location.protocol === "file:" ||
    // Opera returns 0 when status is 304
    ( xhr.status >= 200 && xhr.status < 300 ) ||
    xhr.status === 304 || xhr.status === 1223 ||
    ( xhr.status === 0 && xhr.statusText.toUpperCase() === 'OK');            
Gutzofter
+1  A: 

ponder on moving the setTimeout in the success and error handlers instead and make it asynchronously recursive.

var refreshId = function() {
    $.ajax({
        async: true,
        url: 'gohere',
        dataType: 'text',
        cache: false,
        timeout: 5000,

        success: function(result, textStatus, XMLHttpRequest) {
            alert('textStatus: ' + textStatus + ",\nXHR.readyState: " + XMLHttpRequest.readyState + ",\nXHT.status: " + XMLHttpRequest.status);
            setTimeout(refreshId, 3000);
        },

        error: function(XMLHttpRequest, textStatus, errorThrown) {
            alert('textStatus: ' + textStatus + ",\nXHR.readyState: " + XMLHttpRequest.readyState + ",\nXHT.status: " + XMLHttpRequest.status);
            setTimeout(refreshId, 3000);
        }
    });
};

Thing is, setTimeout and setInterval time is not reliable, especially with alerts. An alert call stalls javascript as long as the alert dialog remains open. Furthermore, setTimeout and setInterval do not interrupt currently running code to guarantee perfect timing, they will wait till the javascript thread has an opening and jump between code executions.

With your previous code setup, you also had a chance that the response of refreshId call 1 will arrive after refreshId call 2 due to the http roundtrip taking an undetermined ammount of time.

By making your setTimeout depend on the response comming in, you get a more stable and transparent result.

BGerrissen
A: 

I believe you should look into http://plugins.jquery.com/project/ajaxqueue which is designed to manage ajax race conditions.

Mark Schultheiss