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

发布于 2024-12-28 21:41:55 字数 3538 浏览 6 评论 0 原文

这是我的 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() 并不能解决问题,它仍然等待这五个请求通过。

为什么会发生这种情况?我该如何修复它?

< strong>编辑:我使用开发人员工具再次运行测试来监视网络活动,结果是:

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

Here is my AJAX function:

/**
 * 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();
};

The general idea is to attempt the request up to five times. Sometimes IE fails with an unusual HTTP error (12xxx), and sometimes the server may fail to respond.

The problem I'm having is that the abort() call doesn't appear to be aborting the connection. To test, I made a simple PHP script:

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

The touch() call creates a file with the current uniqid() - by looking at the modification time I can see the time the sleep(60) ended.

Expected behaviour:

The request is sent
After five seconds, the text changes to "Error... Retying... Attempt 2/5"
Repeat the above up until Attempt 5/5, then fail.
The five calls to the PHP file are aborted, and either there will be five files in the "test" folder, spaced 5 seconds apart, or there will be none because ignore_user_abort is off.

Observed behaviour (in IE9):

The request is sent
The attempt text appears and changes as it should
After five attempts, the error message is displayed
I am unable to load any pages for five whole minutes.
On the server, there are five files spaced one minute apart

I don't know what to make of this, because on the server side Requests 3, 4 and 5 are being sent minutes after the "Timeout" error message is shown on the browser.

If it makes any difference, the page making the AJAX calls is in an iframe. Reloading the iframe (using iframe.contentWindow.location.reload() does NOT fix the issue, it still waits for those five requests to go through.

Why is this happening? How can I fix it?

EDIT: I've run the test again using Developer Tools to monitor network activity. The result is:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

-黛色若梦 2025-01-04 21:41:55

问题似乎是 超时ontimeout 尚未成为某些实现的一部分:

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

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

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

自从 2010 年 9 月 7 日。但是,由于“Level 2”规范自 2008 年 2 月 25 日以来一直存在 并且仍然是一个“工作草案”,并不能真正保证任何实现都符合规范。


如果没有可用的这些,您可以尝试使用 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 次。

The problem seems to be that timeout and ontimeout aren't yet part of some implementations:

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

At least, it's not in Chrome 16 or Firefox 7. And, this should return true given the requirements:

The timeout attribute must return its value. Initially its value must be zero.

Both have been part of the XHR 2 spec since Sept 7, 2010. But, as the "Level 2" spec has been been around since Feb 25, 2008 and is still a "Working Draft," there's not really a guarantee that any implementation would be up-to-date with the spec.


Without those available to you, you can try instead using onabort and setTimeout (as you stated in your comment):

// 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

Example: http://jsfiddle.net/AmQGM/2/ -- The ?delay=2 should finish, while the ?delay=10 expires its 5 tries.

爱给你人给你 2025-01-04 21:41:55

运行您的代码时,我收到错误 c00c023f。当我用谷歌搜索时,它给出了这个答案:

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

这听起来与您所经历的非常相似。

这是一个具有相同问题的问题,并提供了解决方案(也基于上面的链接,但带有附加信息): IE 9 Javascript错误c00c023f

When running your code, I got the error c00c023f. When I googled it, it came up with this answer:

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

This sounds very similar to what you're experiencing.

Here's a SO question which has the same problems, with a solution (also based on the above link, but with additional information): IE 9 Javascript error c00c023f

樱&纷飞 2025-01-04 21:41:55

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

a.onreadystatechange = function() {};

,同时添加对调用 abort 的检查:

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

Before aborting the request, try setting

a.onreadystatechange = function() {};

after that, also add a check around calling abort:

if( a.readyState > 0 && a.readyState < 4 ) {
    a.abort();
}
我只土不豪 2025-01-04 21:41:55

我也遇到了同样的问题,最后发现是 PHP 会话的问题。
如果会话已打开,则来自同一会话的所有其他请求都必须等待。

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

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

I had the same problem, it turned out to be the issue with PHP session.
If the session is open, all other requests from the same session must wait.

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

This, of course, would not help you if you haven't started the session :)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文