异步数据源中存在的问题就是:它们不是同步的。尤其是,通过 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()。但是,这种模式非常容易应用到广泛的相互依存的数据源。
在任何情况下,getResource() 函数都是一个很好的通用框架,它可用于解决在 HTTP 上获取数据时遇到的问题。通常,该访问最终都会成功,前提是在表达请求时没有发生客户端错误。也就是说,短暂的网络和服务器错误只意味着延时,而不是故障。当然,在 Ajax 应用程序中,我们需要使用一些特别的技巧来从多个域中获取数据。其方法通常是将 XMLHttpRequest() 对象放置在单独隐藏的 IFrames 中以轮询不同的域,不过这对异步协调毫无特别之处
如对本文有疑问,请提交到交流论坛,广大热心网友会为你解答!! 点击进入论坛