4

Introduction

This question seems longer than it actually is, but I tried to keep a clean structure.

To start off, I have this webapp which needs to be very small in filesize to be able to load completely (without cache) on your smartphone in < 10s. This requires me to not use a library like jQuery (which doesn't matter anyway, see below).

Without jQuery

I basically used the MDN Ajax page to create this short wrapper:

function ajax(options) {
    var request;

    if (window.XMLHttpRequest) {
        request = new XMLHttpRequest();
    } else {
        request = new ActiveXObject("Microsoft.XMLHTTP");
    }

    request.timeout = 6500;
    request.ontimeout = function () {
        // not called, 6.5s is too high for a timeout anyway, checked this one first
        options.error(request);
    };

    request.onreadystatechange = function () {
        if (request.readyState == 4) {
            if (request.status >= 200 && request.status < 400) {
                options.success(request.responseText);
            } else {
                options.error(request);
            }
        }
    };

    request.open("GET", options.url, true);
    request.send();
}

However, sometimes (in like 1/15 up to to 1/100 cases) the request fails. I get a request object similar to this one:

XMLHttpRequest { 
    onreadystatechange: ajax/request.onreadystatechange(), 
    readyState: 4, 
    timeout: 6500, 
    withCredentials: false, 
    upload: XMLHttpRequestUpload, 
    responseURL: "my-url", 
    status: 0, // <-- this seems to be the main problem
    statusText: "",
    responseType: "",
    response: "" 
}

To point this out, I'm not trying to do CORS here, it's all on the same domain. Also the timeout is set to 6.5s, if I look at the other requests, they usually complete within ~45ms. I tried to "retry" the Ajax 3x if it fails once, to catch the "status = 0"-problem, however this didn't always work out yet as it still occured and 0 means the webserver didn't deliver any content (this workaround just seems like a really dirty hack).

With jQuery Ajax

On another project I'm using jQuery because I don't have these size limits. I'm using a regular jQuery.ajax call, just like this:

$.ajax({
    url: url,
    dataType: "json",
    success: options.updateSuccess,
    error: options.updateError,
    timeout: options.updateTimeout
});

However even this one sometimes fails with statusCode = 0 (the updateTimeout is set to 65s as I "can wait for this one"). Also it's a different webserver than in the first snippet.

What have I tried so far?

  • do some research (turns out many people wanted to use CORS which I'm not trying to do)
  • preventing the browser to use a cached version of the site by appending a timestamp to the url ("&cache=" + new Date().getTime(), didn't work too)
  • check the webserver for wrong configuration (it wasn't the source of this problem, I even tried polling the site with a short Python script in the same intervals like the Ajax calls and they all succeeded)
  • tried different browsers: this mainly happens in Firefox, although Chrome also seems to fail once in a while

Final Question

Why does this happen then? I'm not capable of reproducing it repeatedly, it just sometimes happens. Is this a browser bug that makes every n-th Ajax call fail? Do you have any different approaches to solve this (preferably with plain Javascript)?

Update

I did some more research, this time with wireshark. I logged which request caused the status = 0 and then viewed the TCP flow graph of these packages and all requests got an answer from the server, I guess it's not a server side problem.

michaeln
  • 922
  • 13
  • 31
  • A `status` of `0` indicates that the request was not even sent. Did you double-check the value in `options.url` (what does it say when the error occurs)? It's Javascirpt, so make sure there's no cross access to `options`. – Alex Dec 12 '15 at 23:05
  • I thought so too, but it turns out the url is the correct one. May this be caused by two requests being sent almost at the same time? I have one ajax "thread" (no threads in JS, I think you get what I mean) and one user-initiated ajax. – michaeln Dec 12 '15 at 23:14
  • Check the server request log to see if/how the server handled the request. – Alex Dec 12 '15 at 23:25
  • Status `0` does indicate that the request was sent, but nothing was returned, usually because the request was aborted for some reason. – adeneo Dec 12 '15 at 23:58
  • 1
    http://stackoverflow.com/questions/2000609/jquery-ajax-status-code-0 hope this helps – Rohit Tiwari Dec 14 '15 at 07:21
  • Thanks for that link, but sadly none of the answers match my problem exactly. I did some further research and updated my question. – michaeln Dec 15 '15 at 19:38
  • I solved this somewhat strange issue, thanks for your help everybody! – michaeln Dec 15 '15 at 20:49

1 Answers1

3

So I resolved both problems by doing a lot of tests. The jQuery one was quite easy, updating to jQuery 2.1.4 solved it, I don't want to know what was causing this, it just works now.

For the second problem I reduced the timeout to 500ms and then ran into a timeout which led me to onreadystate being called even though the request didn't finish (in terms of getting an answer in time) completely thus getting status = 0. Why this happened before, see above. To make sure that the timeout was not just "random" I added a retry counter and modified my Ajax function:

function ajax(options) {
    var request;

    if (options.retry === undefined) {
        options.retry = 3; // modify here for default retry count
    }

    if (window.XMLHttpRequest) {
        request = new XMLHttpRequest();
    } else {
        request = new ActiveXObject("Microsoft.XMLHTTP");
    }

    request.timeout = options.timeout ? options.timeout : 500; // modify here for default timeout
    request.ontimeout = function () {
        if (options.retry > 0) {
            options.retry--;
            ajax(options);
        } else {
            options.error(request);
        }
    };

    request.onreadystatechange = function () {
        var delta = new Date().getTime() - request.startTime;

        if (request.readyState == 4) {
            if (request.status >= 200 && request.status < 400) {
                options.success(request.responseText);
            } else if (request.status === 0 && delta >= request.timeout) {
                // do nothing as this was "just a timeout"
            } else {
                options.error(request);
            }
        }
    };

    request.startTime = new Date().getTime();
    request.open("GET", options.url, true);
    request.send();
}
michaeln
  • 922
  • 13
  • 31