xmlhttprequest超时/中止无法按预期工作?

时间:2012-01-24 22:17:39

标签: javascript ajax iframe connection-timeout

这是我的AJAX功能:

/**
 * Send an AJAX request
 *
 * @param url      The URL to call (located in the /ajax/ directory)
 * @param data     The data to send (will be serialised with JSON)
 * @param callback The function to call with the response text
 * @param silent   If true, doesn't show errors to the user
 * @param loader   The element containing "Loading... Please wait"
 */
AJAX = function(url,data,callback,silent,loader) {
    var a,
        attempt = 0,
        rsc = function() {
            if( a.readyState == 4) {
                if( a.status != 200) {
                    if( a.status > 999) { // IE sometimes throws 12152
                        attempt++;
                        if( attempt < 5)
                            send();
                        else if( !silent) {
                            alert("HTTP Error "+a.status+" "+a.statusText+"<br />Failed to access "+url);
                        }
                    }
                    else if(!silent) {
                        alert("HTTP Error "+a.status+" "+a.statusText+"\nFailed to access "+url);
                    }
                }
                else {
                    callback(JSON.parse(a.responseText));
                }
            }
        },
        to = function() {
            a.abort();
            attempt++;
            if( attempt < 5)
                send();
            else if( !silent) {
                alert("Request Timeout\nFailed to access "+url);
            }
        };
    data = JSON.stringify(data);
    var send = function() {
        if( loader && attempt != 0) {
            loader.children[0].firstChild.nodeValue = "Error... retrying...";
            loader.children[1].firstChild.nodeValue = "Attempt "+(attempt+1)+" of 5";
        }
        a = new XMLHttpRequest();
        a.open("POST","/ajax/"+url,true);
        a.onreadystatechange = rsc;
        a.timeout = 5000;
        a.ontimeout = to;
        a.setRequestHeader("Content-Type","application/json");
        a.send(data);
    };
    send();
};

一般的想法是尝试最多五次请求。有时IE会因异常的HTTP错误(12xxx)而失败,有时服务器可能无法响应。

我遇到的问题是abort()调用似乎没有中止连接。为了测试,我制作了一个简单的PHP脚本:

<?php
    sleep(60);
    touch("test/".uniqid());
    die("Request completed.");
?>

touch()调用会创建一个包含当前uniqid()的文件 - 通过查看修改时间,我可以看到sleep(60)结束的时间。

预期行为:

  

请求已发送   五秒钟后,文本变为“错误...退回...尝试2/5”   重复上述步骤,直到尝试5/5,然后失败   对PHP文件的五次调用被中止,并且“test”文件夹中将有五个文件,间隔5秒,或者因为ignore_user_abort关闭而没有。

观察到的行为(在IE9中):

  

请求已发送   尝试文本出现并按原样更改   五次尝试后,显示错误消息
  我整整五分钟都无法加载任何页面   在服务器上,有五个间隔一分钟的文件

我不知道该怎么做,因为在服务器端,请求3,4和5是在浏览器上显示“超时”错误消息后几分钟发送的。

如果它有任何区别,那么进行AJAX调用的页面就在iframe中。重新加载iframe(使用iframe.contentWindow.location.reload()并不能解决问题,它仍会等待这五个请求通过。

为什么会这样?我该如何解决?

编辑:我使用开发人员工具再次运行测试来监控网络活动。结果是:

URL          Method   Result     Type   Received  Taken   Initiator
/ajax/testto          (Aborted)              0 B  < 1 ms (Pending...)
/ajax/testto          (Aborted)              0 B  125 ms (Pending...)
/ajax/testto          (Aborted)              0 B  125 ms (Pending...)
/ajax/testto          (Aborted)              0 B  125 ms (Pending...)
/ajax/testto          (Aborted)              0 B  124 ms (Pending...)

4 个答案:

答案 0 :(得分:4)

问题似乎是timeoutontimeout还不是某些实现的一部分:

var hasTimeout = 'timeout' in new XMLHttpRequest(); // false

至少,它不在Chrome 16或Firefox 7中。而且,根据要求,这应该返回true

  

timeout属性必须返回其值。最初它的值必须为零。

XHR 2 spec以来,两者都是Sept 7, 2010的一部分。但是,由于“{2}”规范自Feb 25, 2008以来一直存在并仍然是“工作草案”,因此并不能保证任何实施都与规范保持同步。


如果没有这些功能,您可以尝试使用onabortsetTimeout(正如您在评论中所述):

// snip

to = function() {
    attempt++;
    if( attempt < 5)
        send();
    else if( !silent) {
        console.log("Request Timeout\nFailed to access "+url);
    }
};

// snip

var send = function() {
    if( loader && attempt != 0) {
        loader.children[0].firstChild.nodeValue = "Error... retrying...";
        loader.children[1].firstChild.nodeValue = "Attempt "+(attempt+1)+" of 5";
    }
    a = new XMLHttpRequest();
    a.open("POST","/ajax/"+url,true);
    a.onreadystatechange = rsc;
    setTimeout(function () {     /* vs. a.timeout */
        if (a.readyState < 4) {
            a.abort();
        }
    }, 5000);
    a.onabort = to;              /* vs. a.ontimeout */
    a.setRequestHeader("Content-Type","application/json");
    a.send(data);
    console.log('HTTP Requesting: %s', url);
};

// snip

示例:http://jsfiddle.net/AmQGM/2/ - ?delay=2应该完成,而?delay=10则会过期5次。

答案 1 :(得分:0)

在中止请求之前,请尝试设置

a.onreadystatechange = function() {};

之后,还要在调用abort时添加一个检查:

if( a.readyState > 0 && a.readyState < 4 ) {
    a.abort();
}

答案 2 :(得分:0)

运行代码时,我收到错误c00c023f。当我用Google搜索它时,它得出了这个答案:

http://www.enkeladress.com/article.php/internetexplorer9jscripterror

这听起来与您所遇到的非常相似。

这是一个具有相同问题的SO问题,有一个解决方案(也基于上述链接,但附加信息):IE 9 Javascript error c00c023f

答案 3 :(得分:0)

我遇到了同样的问题,结果证明是PHP会话的问题。 如果会话已打开,则同一会话中的所有其他请求必须等待。

<?php
    session_write_close();
    sleep(60);
    touch("test/".uniqid());
    die("Request completed.");
?>

如果你还没有开始会议,这当然不会帮助你:)