“HTTP Streaming"的跨浏览器实现(推)AJAX 模式

客户端从服务器请求网页.然后 Clent 要求进行额外的计算;服务器执行一系列计算并在部分结果可用时立即发送(文本格式,每行包含单独的完整项目).客户端使用服务器提供的信息更新网页(使用 JavaScript 和 DOM).

Client request web page from server. Clent then requests for extra calculations to be done; server performs series of calculations and sends partial results as soon as they are available (text format, each line contains separate full item). Client updates web page (with JavaScript and DOM) using information provided by server.

这似乎适合 HTTP Streaming (当前 版本)模式来自 Ajaxpatterns 站点.

This seems to fit HTTP Streaming (current version) pattern from Ajaxpatterns site.

问题是如何以跨浏览器(与浏览器无关)的方式进行,最好不使用 JavaScript 框架,或使用一些轻量级框架,如 jQuery.

The question is how to do it in cross-browser (browser agnostic) way, preferably without using JavaScript frameworks, or using some lightweight framework like jQuery.

问题始于以跨浏览器方式生成 XMLHttpRequest,但我认为主要是并非所有浏览器都能正确实现 onreadystatechangefrom XMLHttpRequest;并非所有浏览器都会在每次服务器刷新时调用 onreadystatechange 事件(顺便说一句.如何从 CGI 脚本(在 Perl 中)强制服务器刷新?).Ajaxpatterns 上的示例代码通过使用计时器来处理这个问题;如果我检测到来自 onreadystatechange 的部分响应,我应该放弃计时器解决方案吗?

The problem begins with generating XMLHttpRequest in cross-browser fashion, but I think the main item is that not all browsers implement correctly onreadystatechangefrom XMLHttpRequest; not all browsers call onreadystatechange event on each server flush (BTW. how to force server flush from within CGI script (in Perl)?). Example code on Ajaxpatterns deals with this by using timer; should I drop timer solution if I detect partial response from onreadystatechange?

添加于 2009 年 8 月 11 日

当前解决方案:
我使用以下函数创建 XMLHttpRequest 对象:

Current solution:
I use the following function to create XMLHttpRequest object:

function createRequestObject() {
        var ro;
        if (window.XMLHttpRequest) {
                ro = new XMLHttpRequest();
        } else {
                ro = new ActiveXObject("Microsoft.XMLHTTP");
        }
        if (!ro)
                debug("Couldn't start XMLHttpRequest object");
        return ro;
}

如果我要使用一些(最好是轻量级的)JavaScript 框架,比如 jQuery,如果用户选择不安装 jQuery,我希望有备用.

If I were to use some (preferably light-weight) JavaScript framework like jQuery, I'd like to have fallback if user chooses not to install jQuery.

我使用下面的代码来启动 AJAX;使用 setInterval 是因为某些浏览器仅在服务器关闭连接后(可能需要数十秒)才调用 onreadystatechange,而不是在服务器刷新数据时(大约每第二次或更频繁).

I use the following code to start AJAX; setInterval is used because some browsers call onreadystatechange only after server closes connection (which can take as long as tens of seconds), and not as soon as server flushes data (around every second or more often).

function startProcess(dataUrl) {
        http = createRequestObject();
        http.open('get', dataUrl);
        http.onreadystatechange = handleResponse;
        http.send(null);

        pollTimer = setInterval(handleResponse, 1000);
}

handleResponse 函数是最复杂的一个,但它的草图如下所示.可以做得更好吗?如何使用一些轻量级的 JavaScript 框架(如 jQuery)来完成?

The handleResponse function is most complicated one, but the sketch of it looks like the following. Can it be done better? How it would be done using some lightweight JavaScript framework (like jQuery)?

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('
');
        nextLine = nextLine + response.lastIndexOf('
') + 1;
        if (response[response.length-1] != '
')
            lines.pop();

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

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

    inProgress = false;
}

推荐答案

实际上,您链接到的解决方案根本不是 AJAX.他们称之为 HTTP 流,但它本质上只是长轮询.

The solution you linked to is not AJAX at all, actually. They call it HTTP Streaming but it's essentially just long polling.

在他们链接到的示例中,您可以很容易地使用 firebug 自己查看.打开网络面板 - 没有 XHR 条目,但加载原始页面只需 10 多秒.那是因为他们在幕后使用 PHP 来延迟 HTML 的输出.这就是长轮询的本质——HTTP 连接保持打开状态,周期性的 HTML 发回是 javascript 命令.

In the example they link to, you can see for yourself quite easily with firebug. Turn on the Net panel - there are no XHR entries, but it takes just a hair over 10 seconds to load the original page. That's because they're using PHP behind the scenes to delay the output of the HTML. This is the essence of long polling - the HTTP connection stays open, and the periodic HTML sent back is javascript commands.

不过,您可以选择使用 setTimeout() 或 setInterval() 在客户端完全进行轮询

You can opt to do the polling completely on the client side, though, with setTimeout() or setInterval()

一个 jQuery 示例

A jQuery example

<script type="text/javascript">
  $(document).ready(function()
  {
    var ajaxInterval = setInterval( function()
    {
      $.getJSON(
        'some/servie/url.ext'
        , { sample: "data" }
        , function( response )
          {
            $('#output').append( response.whatever );          
          }
      );
    }, 10000 );  
  });
</script>

相关文章