跳转到主要内容

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

所有提交的信息确保安全。

  • 关闭 [x]

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

所有提交的信息确保安全。

  • 关闭 [x]

技巧:充分利用异步回调

在 JavaScript 应用程序中实现数据源协调

David Mertz (mertz@gnosis.cx), Impatient data source, Gnosis Software, Inc.
Photo of David Mertz
David Mertz 相信同步就是一切。了解 David 的生活点滴 或者购买他的书籍 Text Processing in Python

简介: 要在 JavaScript 应用程序中充分利用 Ajax 数据源的异步回调,需要使用一些技巧。本文将讨论针对 Ajax 数据源使用异步回调的原因,并将通过一些示例演示如何协调彼此相关的应用程序数据源,使这些数据源在任何时间内都能进行异步调用。

发布日期: 2007 年 12 月 28 日
级别: 中级 其他语言版本: 英文
访问情况 : 3027 次浏览
评论: 


简介

异步数据源中存在的问题就是:它们不是同步的。尤其是,通过 HTTP 协议请求传递的数据可能会远远晚于预期到达,或者请求发生超时,或者完全失败。任何 TCP 层协议都具有不可靠性,但是 Ajax 应用程序可能与多个服务器有数据依赖关系,而这些服务器将影响到整个 Web 应用程序。

处理数据依赖关系并非 Ajax 应用程序的特别之处。各种各样的应用程序都使用信号量、队列、共享变量等在进程中与状态进行通信;在本例中,进程 通常指的是数据获取请求。但是,Web 应用程序中的超时和其他服务器或网络问题多于大多数其他类别的应用程序(尤其是与严格执行本地运行的应用程序相比)。此外,在数据源(甚至同一数据源中的多个请求)中的时序变化方面,基于 Web 的应用程序也要高于大多数其他类型的应用程序,甚至要高于使用基于网络的资源(如数据库)的多数应用程序。


请求状态和超时

我们将遇到的大多数代码示例都是关于异步 Ajax 使用 XMLHttpRequest() 检查是否接收到 200 OK 状态码。我建议在检查的内容中添加一些其他可行的状态,以及一个用于表示完全超时的 “伪状态”。我在近期的一篇 developerWorks 技巧 “使用会话状态避免不必要的 Ajax 流量”(参见 参考资料 获得文章链接)中,我提供了一个利用 304 状态码 的示例,该示例仍然是个不错的想法。

适当处理各种 HTTP 状态码是个不错的想法。针对 2xx 状态码(而不是 200 OK)的操作仍然有点悬而未决:比如说,如果应用程序将创建一个服务器资源,那么您可能希望检查 201 Created 状态码并查看响应中的 URI。标准情况下,客户机必须 透明的重定向状态码 301、302、303 和 307。因此,我们实际上不必担心它们,因为它们最终将得到 200 状态(或者是超时、4xx 和 5xx 状态)。区别处理 4xx 错误和 5xx 错误可能是一个很好的方法。概括地说,4xx 状态码(尤其是常见的 404 Not Found 错误)可能表示客户机所使用 URL 中存在一些问题。5xx 错误表示服务器存在问题,通常只需等待一段时间并重试便可解决该问题。

与处理各种服务器错误同样重要的情况是,有时服务器会无限期挂起,而根本不会返回任何响应。挂起实质上等同于 5xx 状态码,不同之处就是我们无法检查 XMLHttpRequest().status 状态码来识别它们。为此,我们可以使用一个 setTimeout() 计时器来取消超时请求。

将相关检查结合在一起,我们的 Ajax 数据源请求可能类似于清单 1:


清单 1. 数据源请求的健壮处理
function getResource(uri, data_callback, error_callback, timeout) {
    var tryAgain = function () {
      getResource(uri, data_callback, error_callback, timeout);
    }
    var r = new XMLHttpRequest();
    var timer = setTimeout(
        function() {
            r.abort();
            r.onreadystatechange = null;
            setTimeout(tryAgain, timeout);
        },
        timeout);
    r.open("GET", uri, true);
    r.onreadystatechange = function() {
        if (r.readyState != 4) {
            // Ignore non-loaded readyStates
            // ...will timeout if do not get to "Loaded"
            return;
        }
        clearTimeout(timer);  // readyState==4, no more timer
        if (r.status==200) {  // "OK status"
              data_callback(r.responseText);
        }
        else if (r.status==304) {
            // "Not Modified": No change to display
        }
        else if (r.status >= 400 && r.status < 500) {
            // Client error, probably bad URI
            error_callback(r)
        }
        else if (r.status >= 500 && r.status < 600) {
            // Server error, try again after delay
            setTimeout(tryAgain, timeout);
        }
        else {
            error_callback(r);
        }
    }
    r.send(null);
    return r;
}

当然, 我们可以在 getResource() 中加入各种增强。比如说,在超时或服务器错误状态事件中,我们可以在延时之后调用 tryAgain()。或者,我们可能希望在这两种情况下使用一些其他的回调。传递的 error_callback() 函数将用于下面这种情况,即不希望再次发送重复请求,但是常常会发生这种情况。


数据协调

给出的 getResource() 函数可能会无限制地等待,获取数据源并将其提供给 data_callback()。但是,该数据回调自己可能需要依赖其他数据的可用性。同时操作多个数据源的最简单方法就是将它们的内容存储在一个全局变量中,并当该数据被使用时清空这些变量。比如说,基本的回调方法可能类似于清单 2:


清单 2. 两个源的基本数据回调
var other_data = null;
function processOtherData(responseText) {
    other_data = responseText;
}
function processData(this_data) {
    var delay = 1000;     // Keep trying at 1-second intervals
    if (other_data == null) {
        setTimeout(function() { processData(this_data); }, delay);
        return;
    }
    // We have both this_data and other_data
    displayThisAndThat(this_data, other_data);
    // Reset other_data now that we have consumed it
    other_data = null;
}

应用程序将调用 getResource(uri1,processOtherData,...) 方法, 然后将在别处调用 getResource(uri2,processData,...) 方法。后面这个调用将运行一个回调从内部 “轮询” other_data 直到它实际填入了数据,并在该数据被处理后将它清空。另一个信号量可能与以下操作相关,避免重复后面这个调用,直到第一次尝试成功并且/或针对延时的 processData() 调用 clearTimeout(),有利于更新的请求。


结束语

数据协调的具体应用在任何种类的应用程序中的复杂度都会有所不同 — 而不仅仅是 Ajax 应用程序。本文所提供的示例只使用一个主数据表示函数 processData() 和一个有用的回调来获取额外的数据,即 processOtherData()。但是,这种模式非常容易应用到广泛的相互依存的数据源。

developerWorks Ajax 资源中心
访问 Ajax 资源中心,其中提供了关于开发 Ajax 应用程序的各种免费工具、代码和信息。由 Ajax 专家 Jack Herrington 管理的 动态 Ajax 社区论坛 为 Ajax 开发人员提供了相互交流和解决问题的平台。

在任何情况下,getResource() 函数都是一个很好的通用框架,它可用于解决在 HTTP 上获取数据时遇到的问题。通常,该访问最终都会成功,前提是在表达请求时没有发生客户端错误。也就是说,短暂的网络和服务器错误只意味着延时,而不是故障。当然,在 Ajax 应用程序中,我们需要使用一些特别的技巧来从多个域中获取数据。其方法通常是 将 XMLHttpRequest() 对象放置在单独隐藏的 IFrames 中以轮询不同的域,不过这对异步协调毫无特别之处。


参考资料

学习

讨论

  • MochiKit 库拥有一个相当不错的异步框架,可用于推迟任何函数的调用,而并不仅限于等待 XMLHttpRequest() 对象。但是对于该技巧,我们只关心数据请求,而不是长时间运行的计算。了解 MochiKit.Async

关于作者

Photo of David Mertz

David Mertz 相信同步就是一切。了解 David 的生活点滴 或者购买他的书籍 Text Processing in Python

关于报告滥用的帮助

报告滥用

谢谢! 此内容已经标识给管理员注意。


关于报告滥用的帮助

报告滥用

报告滥用提交失败。 请稍后重试。


developerWorks:登录


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 使用条款

 


当您初次登录到 developerWorks 时,将会为您创建一份概要信息。您在 developerWorks 概要信息中选择公开的信息将公开显示给其他人,但您可以随时修改这些信息的显示状态。您的姓名(除非选择隐藏)和昵称将和您在 developerWorks 发布的内容一同显示。

请选择您的昵称:

当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

(长度在 3 至 31 个字符之间)


单击提交则表示您同意developerWorks 的条款和条件。 使用条款.

 


为本文评分

评论

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Web development, Java technology, XML
ArticleID=278861
ArticleTitle=技巧:充分利用异步回调
publish-date=12282007
author1-email=mertz@gnosis.cx
author1-email-cc=ruterbo@us.ibm.com

标签

Help
使用 搜索 文本框在 My developerWorks 中查找包含该标签的所有内容。

使用 滑动条 调节标签的数量。

热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。

我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。

使用搜索文本框在 My developerWorks 中查找包含该标签的所有内容。热门标签 显示了特定专区最受欢迎的标签(例如 Java technology,Linux,WebSphere)。我的标签 显示了特定专区您标记的标签(例如 Java technology,Linux,WebSphere)。