
就好像排队,前面的人忙着忙着突然上厕所了,后面的人阻塞在这里,因此我们就需要让前面的人死到一边去,让后面的人跟进……AJAX就是这个概念,请求还在继续,但我们还可以做其他事。
javascript中实现这个功能的是来自BOM的一个函数setTimeout,但相关的DOM操作也提供了一系列实现。如XMLHttpRequest对象与script标签的onreadystatechange回调,image的onload与onerror回调,iframe的onload,DOM元素的事件回调,HTML5的跨域消息传送postMessage,QuickTime与flash对象的加载……
setTimeout的零秒延迟在前些年时间被国内宣扬得特别厉害,但setTimeout是所有延迟中最慢的,最少要花上10多毫秒,如果用setTimeout来开发特效,这特效会运行得比较慢。下面是一个性能测试:
[Ctrl+A 全选 注:如需引入外部Js需刷新才能执行]
|
Chromium |
Safari |
Firefox |
Opera 10.10 |
Opera 10.50 |
setTimeout |
4.32ms |
10.201ms |
10.302ms |
10.38ms |
9.876ms |
img.onerror |
0.199ms |
0.678ms |
0.201ms |
0.058ms |
0.575ms |
script.onreadystatechange |
fail |
fail |
fail |
fail |
fail |
script.onload |
0.414ms |
0.138ms |
0.414ms |
fail |
fail |
xhr.onreadystatechange |
fail |
0.622ms |
fail |
0.078ms |
0.079ms |
self.postMessage |
0.096ms |
0.123ms |
0.112ms |
0.049ms |
0.094ms |
为了处理这种异步调用,Mochikit从Python的Twisted框架借来了Deferred这个类,并用它来处理AJAX的回调。AJAX的回调通常有两种,成功加载时的回调与请求失败的回调,IE8的XDomainRequest就有这两种回调了,标准浏览器的script与image也有这两种回调,分别称之为onload与onerror。Mochikit的Deferred实例就内置一个数组,每次包含这两种回调,依次执行。Mochikit这伟大的遗产后来由dojo发扬光大了,至于怎么用,自己google吧。
下面是我的框架对它的应用,我已把它整合到我的框架中:
代码如下:
异步操作例子 by 司徒正美 <%= javascript_include_tag "dom.js" %>
<%= javascript_tag "window._token = '#{form_authenticity_token}'" if ActionController::Base.allow_forgery_protection %>
dom.ready(function(){
dom.require("ajax");
dom.ajax({method:"post",
async:true,
dataType:"text",
data:{authenticity_token:window._token}
}).next(function(a){
alert(a)
});
dom.jsonp({url:"http://del.icio.us/feeds/json/fans/stomita"}).next(function(json){
alert(json)
}).error(function(e){
alert(e)
});
});
script>
后台:
代码如下:
class HomeController < ApplicationController
def index
if request.xhr?
name = params[:name]
puts "-------------"
render :text => "
The time is " + DateTime.now.to_s + "
"
end
end
end
在日本一博客提到这样一个捕捉异步错误的例子:
代码如下:
function throwError(){
throw new Error('ERROR');
}
try{
setTimeout(throwError, 3000);
} catch(e){
alert(e);
}
看来try...catch是无法捕捉这种形式的错误,window.onerror可以,但好像只有IE与FF支持。如果用Deferred来处理,就简单了!
代码如下:
dom.Deferred.next(function () {
throw new Error("错误")
}).wait(1).error(function(e){
alert(e instanceof Error)
});
列队处理。由于是使用了异步,因此不会阻塞页面的演染。
代码如下:
异步操作例子 by 司徒正美 <%= javascript_include_tag "dom.js" %>
dom.require("deferred");
dom.require("query");
dom.ready(function(){
var a = dom("#aaa")[0];
dom.Deferred.loop(10,function(i){
a.innerHTML += i+"
"
});
dom.Deferred.loop(10,function(i){
a.innerHTML += String.fromCharCode(i+97)+"
"
});
dom.Deferred.loop(10,function(i){
a.innerHTML += "司徒正美"+i+"
"
});
});
/*结果
0
a
司徒正美0
1
司徒正美1
2
c
司徒正美2
3
d
司徒正美3
4
e
司徒正美4
5
f
司徒正美5
6
g
司徒正美6
7
h
司徒正美7
8
i
司徒正美8
9
j
司徒正美9
*/
script>