在Chrome中加载(readyState == 3)时XmlHttpRequest.responseText

时间:2010-10-07 09:36:56

标签: javascript ajax google-chrome streaming xmlhttprequest

我试图通过ajax(通过XmlHttpRequest(= xhr)在Javascript中“流式传输”(从服务器到客户端)。我使用的是修改后的handleResponse函数 Cross-browser implementation of "HTTP Streaming" (push) AJAX pattern

function handleResponse() {
if (http.readyState != 4 && http.readyState != 3)
    return;
if (http.readyState == 3 && http.status != 200)
    return;
if (http.readyState == 4 && http.status != 200) {
    clearInterval(pollTimer);
    inProgress = false;
}
// In konqueror http.responseText is sometimes null here...
if (http.responseText === null)
    return;

while (prevDataLength != http.responseText.length) {
    if (http.readyState == 4  && prevDataLength == http.responseText.length)
        break;
    prevDataLength = http.responseText.length;
    var response = http.responseText.substring(nextLine);
    var lines = response.split('\n');
    nextLine = nextLine + response.lastIndexOf('\n') + 1;
    if (response[response.length-1] != '\n')
        lines.pop();

    for (var i = 0; i < lines.length; i++) {
        // ...
    }
}

if (http.readyState == 4 && prevDataLength == http.responseText.length)
    clearInterval(pollTimer);

inProgress = false;
}

使用php脚本,它可以刷新数据(没有ajax,它会在进行过程中将数据刷新到浏览器中)

我在Firefox中没有问题,但谷歌Chrome和IE给我一个空的responseText而xhr.readyState等于3.我发现在互联网上描述了这个问题,但它没有给我任何解决方案。

您知道吗,如何在Chrome中解决此实施问题? (w3c说,readyState中的responseText不能为NULL == 3 - Chrome实现了这个规则,但只给出了空字符串)

如果你不知道,你知道某些产品的工作解决方案吗? (开源框架,图书馆等)

非常感谢你的想法。

修改 解决方法是创建iframe,将脚本调用到iframe并在此处刷新数据,并通过jrame从iframe获取数据。但这不是ajax解决方案。我真的很想看到纯粹的ajax解决方案。

11 个答案:

答案 0 :(得分:17)

Chrome有一个错误,它只会在收到一定数量的字节后填充xhr.responseText。有两种方法可以解决这个问题,

将返回的内容类型设置为“application / octet-stream”

发送约2kb的前奏来准备处理程序。

当readyState == 3时,这些方法中的任何一个都应该使chrome填充responseText字段。

另一方面IE7 / 8无法做到,你需要求助于长时间轮询或在IE8中使用XDomainRequest的跨域技巧,la MS

答案 1 :(得分:3)

您是否考虑过使用WebSocketsserver-sent events

Most major browsers现在支持WebSocket协议,但如果您的网站需要在IE 9或更早版本中运行,或者在Android浏览器4.3或更早版本中运行,则必须保留使用XMLHttpRequest作为后备的代码。 / p>

Most of these browsers还支持一个名为服务器发送事件的功能,它与WebSockets不同,可以使用传统的HTTP守护程序和CGI / PHP脚本在服务器上实现,但只提供一个 - 通信。

另请参阅:WebSockets vs. Server-Sent events/EventSource

答案 2 :(得分:3)

为了扩展安德鲁的答案,这是我提出的跨浏览器解决方案。

在99%的浏览器中正常运行,即IE≥8,Chrome,Firefox和Safari ,一旦浏览器收到数据就会发送增量事件(但请参阅下面的注释。)

if (/MSIE [8-9]/.test(navigator.appVersion)) {
    var get = new XDomainRequest()
    get.onprogress = handleData
    get.onload = handleData
} else {
    var get = new XMLHttpRequest()
    get.onreadystatechange = handleData
}
get.open('get', '/example/url')
get.send()

function handleData() {
    if (get.readyState != null && (get.readyState < 3 || get.status != 200)) {
        return
    }
    // process incremental data found in get.responseText
}

IE 8-9将在2kB数据后开始填充responseText,所以如果不行,你应该发送一个2kB的初始填充。

Chrome需要或Content-Type: application/octet-stream

答案 3 :(得分:2)

嗯,不幸的是,XmlHttpRequest(或任何Web标准)的每个部分都没有在所有浏览器中完全实现。但是您还有其他几种HTTP Streaming选项:
  Wikipedia: Push technology
  Wikipedia: Comet (programming)   Wikipedia: Web Sockets(实验性,低浏览器支持)

我在你的评论中看到你希望它是纯粹的AJAX,但我想建议可能的替代解决方案。您可以在可能的情况下使用JavaApplet或使用Flash对象。对于后者,您不需要华而不实且昂贵的IDE,您可以使用Haxe创建Flash / SWF文件,并且您会对JavaScript感到非常熟悉。

这是Flash/Neko chat example,可能也可以用于其他平台和用法。

祝你好运。

答案 4 :(得分:1)

在IE中尝试使用responseStream / responseBody属性。我想过做一次类似的事情并遇到同样的问题。不幸的是w3c规格都不是

http://www.w3.org/TR/XMLHttpRequest/#the-responsetext-attribute

答案 5 :(得分:1)

据我所知,在readyState 3上提供部分文本是一种非标准的firefox行为,根本无法直接在其他浏览器中模拟,你可能想要做的是对小块进行多个顺序请求数据而不是一个“流媒体”请求

答案 6 :(得分:1)

这适用于Chrome,但不适用于IE:

[test.php的]:

<?php
Header('Content-type: text/plain');
while (1) {
    echo str_pad('test: '.mt_rand(1000,9999), 2048, ' ');
    flush();
    sleep(1);
}

[的test.html]:

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Stream test</title>
        <meta charset="UTF-8" />
        <script type="text/javascript">
            function xmlHttpRequest() {
                return (function (x,y,i) {
                    if (x) return new x();
                    for (i=0; i<y.length; y++) try { 
                    return new ActiveXObject(y[i]);
                    } catch (e) {}
                })(
                    window.XMLHttpRequest, 
                    ['Msxml2.XMLHTTP','Microsoft.XMLHTTP']
                );
            };
            function stream(url) {
                // Declare the variables we'll be using
                var xmlHttp = xmlHttpRequest();
                xmlHttp.open("GET", url, true);
                var len = 0;
                xmlHttp.onreadystatechange = function() {
                if (xmlHttp.status == 200 && xmlHttp.readyState >=3) {
                    var text = xmlHttp.responseText;
                    text = text.substr(len, text.length-len);
                    len = xmlHttp.responseText.length;
                    console.log(text);
                }
                }
                xmlHttp.send(null);
            }           
            stream('/test.php');
        </script>
    </head>
    <body>
    </body>
</html>

因人而异。

答案 7 :(得分:1)

正如Jaroslav Moravec所说,如果你将流的标题中的内容类型设置为application / x-javascript,它可以在Safari,Chrome和Firefox中使用。

我没有测试IE。

答案 8 :(得分:1)

按照Andrew的建议将返回的内容类型设置为“application / octet-stream”是一个很好的解决方案。 另外,你应该在IE上使用XDomainRequest。

要读取数据,您应该使用无限循环(当readystate = 4或XDomainRequest.onLoad被调用时停止)并且超时。

我将如何做到这一点:

var i = 0;
var returnValue = function() {
    if (!finished) {
        setTimeout(returnValue, 100);
    }
    var resp = ajax.responseText;
    var newI = resp.lastIndexOf(";") + 1;
    if (newI > i) {
        var lines = resp.substring(i, newI).split(";");
        for (var x = 0; x < lines.length; x++) {
            eval(lines[x]);
        }
        i = newI;
    }
}

注意:有人说使用eval是有风险的,我声称这不是真正来自风险的地方。

答案 9 :(得分:0)

您提交请求的网址是 - 它是您网域的一部分吗?

这可能是因为same origin policy

请参阅此questionpossible ways to circumvent it(以及此article)。

答案 10 :(得分:0)

一旦我使用safari遇到这个问题(从未测试过使用chrome,也许有同样的问题(chrome / safari都使用相同的渲染引擎(据我所知) - 不知道js-parts ))。我从来没有找到解决方案来解决这个问题,但由于它是公司范围的内部网中的一个小应用程序,因此不支持safari(ff是默认浏览器,并且ff正常工作)并不是一个大问题。 / p>