终所周知,在使用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 ); /* 否则,继续检查 */
}
};
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 */
}
};
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 >
< 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 >