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

发布于 2024-07-27 19:12:04 字数 3022 浏览 3 评论 0 原文

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

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

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

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


已于 2009 年 8 月 11 日添加

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

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 时有后备。

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

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

        pollTimer = setInterval(handleResponse, 1000);
}

handleResponse 函数是最复杂的一个,但它的草图如下所示。 可以做得更好吗? 使用一些轻量级 JavaScript 框架(如 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('\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;
}

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.

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

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.

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?


Added 11-08-2009

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;
}

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.

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);
}

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('\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;
}

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

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

发布评论

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

评论(2

荒岛晴空 2024-08-03 19:12:04

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

在他们链接到的示例中,您可以使用 firebug 轻松地亲自查看。 打开Net面板——没有XHR条目,但加载原始页面只需要10多秒的时间。 这是因为他们在幕后使用 PHP 来延迟 HTML 的输出。 这就是长轮询的本质 - HTTP 连接保持打开状态,并且定期发回的 HTML 是 javascript 命令。

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

一个 jQuery 示例

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

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

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.

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

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>
沉睡月亮 2024-08-03 19:12:04

我会看一下 Orbited

他们使用几种彗星传输实现,他们根据配置和浏览器嗅探选择这些实现。

请参阅http://orbited.org/svn/orbited/trunk /daemon/orbited/static/Orbited.js

并查找“Orbited.CometTransports”

一些不同的传输必须由后端实现匹配,因此也请查看服务器端的 Orbited。

I would take a look at orbited

They use several comet transport implementation that they choose based on configuration and browser sniffing.

See http://orbited.org/svn/orbited/trunk/daemon/orbited/static/Orbited.js

and look for "Orbited.CometTransports"

Some of the different transports must be matched by the backend implementation, so have a look at the server side for orbited also.

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