终所周知,在使用xmlHttpRequest进行异步调用时,浏览器会给每个请求分配一个独立线程(进程?没细去研究) ,当请求成功(或者失败后)再响应回调函数。当遇到某些ui部分的操作必须等在异步请求结束后才能进行的情况下,一般处理方式有两种:1、把界面部分的操作函数做为回调函数;2、改用同步模式。先来批判下第二种方法,这样无疑失去了ajax精髓的部分,所以不提倡。那么第一种方法呢?举个例子,对界面部分的操作写成了一个类,通过类的方法来进行调用,那此时,不得不把类的初始化,类的方法调用写到回调函数里。再举个例子,直接在这个类里编写实现异步调用的方法(也就是异步调用和ui操作是同一个类的不同方法)。虽然,这些都是普遍的做法,但是,这恰恰已经使界面操作和控制器部分混合在了一起,不符合mvc的思想,当然也并不利于重用。
那如何来解决这个后遗症呢?设想下,问题是必须等到异步调用结束后,才能进行相应的其他操作,那么如果,设立一个独立的进程来检查异步调用是否已经结束,当结束后通知需要进行操作的方法再来执行。注意,这和用同步调用是有区别的。因为同步调用是阻塞了整个浏览器的进程。而这种用独立进程来检查的方式,并不影响其他异步调用。为此我对症下葯,实现了如下这个类:(需要prototype框架支持)
var xRouter=Class.create();
xRouter={
routers:[], /*数组,用于存放请求*/
/*注册请求,第一个参数为类实例或者函数引用,第二参数指示该请求的行为,是异步处理还是同步处理*/
/*
如果请求是类,类中必须实现load方法,用来启动类的各种操作,并且如需要进行同步处理时,
在适当的位置赋state字段为‘complete’,来指示此类已经调用结束。
*/
register:function(application,asy) {
if (!asy) asy=false;
Object.extend(application,{$__asy:asy});
if (!this.routers.include(application))
this.routers.push(application);
},
/*
注销请求,注意,请求必须是类的实例引用或者已声明函数的引用
*/
unregister:function(application) {
this.routers=this.routers.without(Object.extend(application,{$__asy:true}));
this.routers=this.routers.without(Object.extend(application,{$__asy:false}));
},
/*
轮讯各个请求,实现各个请求的异步调用和同步调用
*/
dispatch:function() {
this.routers.findAll(function(app){
return (app.$__asy);
}).each(function(app) {
if (typeof app=='object')
{
app.load();
}
else if(typeof app=='function')
{
app.call(this);
}
this.unregister(app);
}.bind(this));
this.router();
},
/*内部函数,当注册的请求没有需要异步处理时,用户也可以直接调用此函数进行同步处理*/
router:function() {
if (this.routers.length>0)
{
var app=this.routers.shift();
if (typeof app=='function' && !app.$__asy)
{
app.call(this);
this.router();
}
else if (typeof app=='object' && !app.$__asy)
{
app.load();
app.base=this; /*绑定this对象,以便其后调用*/
setTimeout(this.checkState.bind(app),1); /*用单独线程检查类的异步调用是否完成*/
}
else
{
this.router();
}
}
},
checkState:function() {
if (this.state && this.state.toLowerCase()=='complete')
this.base.router(); /*如果类的异步调用已经完成,则继续下一个需要同步处理的类或者函数*/
else
setTimeout(this.base.checkState.bind(this),1); /*否则,继续检查*/
}
};
为了更加直观的理解此方法,下面再重新组织一下xmlHttpRequest(同样需要prototype框架支持)
var xLoader=Class.create();
xLoader.prototype={
initialize:function(url,options) {
this.url=url; /*请求后台模型或者页面的地址*/
this.options=Object.extend{
{
method:'get',
parameters:null
}
,options || {}); /*用户自定义参数*/
this.loader=null; /*xmlHttpRequest请求加载实例*/
this.state=null; /*类当前状态*/
this.isSuccess=false; /*指示异步调用是否成功*/
this.request; /*返回的request对象*/
},
/*类启动*/
load:function() {
var url=jsEvent.noCache(this.url); /*一个防止页面缓存的处理函数,可以忽略*/
this.loader=new Ajax.Request(
url,
{
method:this.options.method,
parameters:this.options.parameters,
onSuccess:this.successHandler.bind(this),
onFailure:this.failureHandler.bind(this)
}
);
},
successHandler:function(request) {
this.isSuccess=true;
if (this.options.onSuccess) this.options.onSuccess.call(this,request); /*调用用户自定义的函数*/
this.completeHandler();
},
failureHandler:function(request) {
this.isSuccess=false;
if (this.options.onFailure) this.options.onFailure.call(this,request); /*调用用户自定义的函数*/
this.completeHandler();
},
completeHandler:function(request) {
if (this.options.onComplete) this.options.onComplete.call(this,request); /*调用用户自定义的函数*/
this.request=request;
this.state=Ajax.Request.Events[this.loader.transport.readyState]; /*记录类的状态为complete*/
}
};
举例:
<script type="text/javascript" src="lib/prototype-1.4.0.js"></script> <!--需要prototype框架支持-->
<script type="text/javascript">
//<![CDATA[
var xmlDoc;
window.onload=function() {
xRouter.register(new xLoader('test1.xml',{onComplete:test1}),false);
xRouter.register(new xLoader('test2.xml',{onComplete:test2}),true);
xRouter.register(test3,true);
xRouter.register(test4,false);
/*按照注册请求的顺序,处理其中需要同步处理的请求*/
xRouter.dispatch();
}
function test1(request) {
alert('同步处理对test1.xml的异步请求');
xmlDoc=request.responseXML;
}
function test2() {
alert('异步处理对test1.xml的异步请求');
}
function test3() {
alert('异步处理test3函数');
}
var test4=Class.create();
test4.prototype={
initialize:function() {
alert('同步处理对test1.xml异步请求结束后的test4类');
this.state=null;
},
load:function() {
alert(xmlDoc.xml);
this.state='complete';
}
}
//]]>
</script>