锁定老帖子 主题:[原创]伪同步调用ajax(伪阻塞)
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2008-06-30
最后修改:2009-10-09
如果在一个函数中,我们需要的数据是要从ajax获取。如果用ajax同步 的方法,浏览器会挂起,这是一个很讨厌的事情。 而ajax异步 在浏览器中的实现是立即返回的,而我们要的数据还没有被获取。
那用ajax异步 的方法怎么做才能达到我们的目的呢?让我们一步步实现伪同步. 先给个简单的方法: function foo(data){ if(undefined==data) return ajaxQueue({...},{callback:arguments.callee}); ......someting...... } 也就是说: 函数foo内部有ajax异步(ajaxQueue就是入口)的请求,而且ajax后的结果对foo的内部(someting)执行有影响,因此需要在foo内部进行判断是否ajax异步请求已经完成 ,或者说调用者是否是ajax异步请求 . 上面代码中用了一个简单的判断 if(undefined==data) 而 {callback:arguments.callee}
就是告诉ajaxQueue取到数据后,再回调foo了. 不过这个方法有问题,也就是借用了foo 函数的参数值,在某些场合也许就不适用了。因此这个方法是有问题的,ajaxQueue的实现也就失去了讨论的意义。
首先让我们完善上面的方法:注意闭包的用法(也不知道会不会造成内存泄漏) function ajaxCallback(queue,args){ var func=arguments.callee.caller; var length=queue.length; for (var i=0;i<queue.length;i++) (function(){ var o=queue[i]; var _old = o.complete; o.complete = function(){ if ( _old ) _old.apply( this, arguments ); length--; if (0==length) { func.apply(ajaxCallback,args); } }; jQuery.ajax(o); })(); }
调用: function foo(ddd){ if(this!==ajaxCallback){ return ajaxCallback([ {url:'/1.txt',complete:function(){alert(1);}}, {url:'/1.txt',complete:function(){alert(2);}}], arguments); } alert(arguments.length); alert(ddd) } foo(3);
这里面还有一个问题,就是虽然没有借用参数,但是借用了this(我是不是绕的弯弯太多了,直奔正解不就行了,其实这是我解决这个时候的思路,自己记录一下).对OO的编程可就有产生了新的麻烦。 改一下: function ajaxCallback(queue,args){ var func=arguments.callee.caller; var length=queue.length; for (var i=0;i<queue.length;i++){ (function (){ var o=queue[i]; var _old = o.complete; o.complete = function(){ if ( _old ) _old.apply( this, arguments); length--; if (0==length) { Callback(func,args[0],args[1]); } }; jQuery.ajax(o); })(); } } function Callback(func,self,args){ func.apply(self,args); }
测试: function foo(ddd){ if(arguments.callee.caller!=Callback){ return ajaxCallback([ {url:'/1.txt',complete:function(){alert(1);}}, {url:'/1.txt',complete:function(){alert(2);}}], [this,arguments]); } alert(ddd); alert(this); } foo(3); var f={}; f.foo=foo; f.foo(3); 这回好了。不占参数,this也得到了正确(其实是调用的时候主动传过去的)传递. ajax 伪同步实现. 备注 :您需要明白的是,我这里说的同步指的是调用ajax的函数实现了伪阻塞,关于多ajax请求伪同步的方法有队列法,比如jQuery的插件ajaxQueue ,从某种意义上说我的这个方法也可以实现,不过既然人家工作的很好,就不费这个力气了. 暂时思路只能到这里,进一步要靠实践了.
更新.感觉函数的名字起的不好,改了下,而且增加了不需要回调的判断: //多ajax请求队列,支持完结回调 function ajaxQueue(queue,thiss,argss,other){ if (arguments.length<1) return false; if (!argss) argss=[]; var args=[]; for (var i=0;i<argss.length ;i++ ) args.push(argss[i]); if (other) for (var i=0;i<other.length ;i++) args.push(other[i]); var func=arguments.callee.caller; (function (a,func,thiss,args){ var o=a.shift(); //增加了判断是否要回调 if (!o) return thiss?thisCallback(func,thiss,args):false; var self=arguments.callee; var _old = o.complete; o.complete = function(){ if ( _old ) _old.apply( this, arguments); self(a,func,thiss,args); }; jQuery.ajax(o); })(queue,func,thiss,args); return false; } //回调函数,保持原来的this指针 function thisCallback(func,thiss,args){ if (func.constructor == Function) func.apply(thiss,args); return false; } 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2008-07-02
终于想明白自己要说什么了,也找到了合适的词汇,并重新编辑了帖子。
|
|
返回顶楼 | |
发表时间:2008-07-03
根据前面的思考重新写了一个伪同步回调模式的ajax队列函数:可以完全替代jQuery插件ajaxQueue了,而且功能更强. //回调函数,保持原来的this指针 function thisCallback(func,thiss,args){ func.apply(thiss,args); return false; } //把arguments分成argss,other两部分完全是为了调用方便.方便到哪里了你用了就明白了. function ajaxQueue(queue,thiss,argss,other){ if (arguments.length<3) return false; var args=[]; for (var i=0;i<argss.length ;i++ ) args.push(argss[i]); if (other) for (var i=0;i<other.length ;i++) args.push(other[i]); var func=arguments.callee.caller; (function (a,func,thiss,args){ var o=a.shift(); if (!o) return thisCallback(func,thiss,args); var self=arguments.callee; var _old = o.complete; o.complete = function(){ if ( _old ) _old.apply( this, arguments); self(a,func,thiss,args); }; jQuery.ajax(o); })(queue,func,thiss,args); return false; } 调用的例子:动态执行服务器端的js //参数a是要动态请求的服务器端的js文件 function test(a){ if(arguments.callee.caller==thisCallback){ alert('ok'); return; } var queue=[]; for(var i=0;i<a.jslist.length;i++){ queue.push({url:a.jslist[i],success:function(js){ try{ window.eval(js); }catch(ex){//错误处理代码,这个要看你的处理了 $('#error').append('<pre>Error:\n'+ex.message + '\n'+ (ex.lineNumber || ex.number)+'</pre>'); } }}); } ajaxQueue(queue,this,['ajaxQueue']); return; } 当然还可以设计出success出错后的中断队列的模式,暂时不写了,因为很容易,等我的应用有需求的时候再写吧. |
|
返回顶楼 | |
发表时间:2008-07-15
好象并不能解决我的问题,因为我的问题本身是有问题的,无法解决,你的想法很好,应该对于其他问题很有用
|
|
返回顶楼 | |
发表时间:2008-07-15
gui1401 写道 好象并不能解决我的问题,因为我的问题本身是有问题的,无法解决,你的想法很好,应该对于其他问题很有用
我正在写的nicEdit 打包器 用的就是这个方法+jCT做的,现在还没有做完,感兴趣可以看看,有实例参考也许会对你有启发. 源文件目录 http://ne.16lo.com/src/nicEditor/ achun.html就是jCT模板, src/nicEditor/src 目录是nicEditor的源文件 _.php是入口文件,不允许浏览器访问,内容如下: foreach ($AQ as $Q=>$P){ switch ($Q){ case 'entry': se_Msg(se_webPath(dirname(__FILE__))); break; } } 其实就是输出一个相对浏览器的entry URL,就是他了 {"entry":"/src/nicEditor"} 我的服务器目录是配置了只有src下的才能浏览, 使用FF+FireBug可以监视到整个实现. |
|
返回顶楼 | |
发表时间:2009-05-08
看不懂怎样使用,能否给一些使用的演示代码
|
|
返回顶楼 | |
发表时间:2009-05-08
最后修改:2009-05-08
zlt2000 写道
看不懂怎样使用,能否给一些使用的演示代码
晕呀,那个打包工具的代码没有了,链接当然也无效了。 其实这就是一个ajax获取数据的工具,我的应用中需要多次向后台进行ajax请求,而这些请求必须是按次序进行的。 举一个实际的例子吧 您如果看了我的博客的话,会发现jCT 这个前台模板工具。对于模板来说有两部分 1.是模板文件 2.对应的数据 对于前台模板工具来说,这两部分要分别从后台获取的,如果采用ajax方法取这两个数据的时候,可以发出两个ajax请求,但是在任何一个请求完成以后就要判断另一个是否也完成了,这有些麻烦。 如果采用我上面说的为阻塞,也就是ajax队列的方法就不存在这个问题,因为事实上每次只有一个ajax的请求被发出,队列中的请求是顺序执行的。
比如有一个列表的模板文件 /template/list.html 而对应的后台数据请求是 /?getlist=99 处理这两个数据的对象是 obj={ template:function(){}, listdata:function(){} } 那调用代码可以是这样 ajaxQueue('/template/list.html',obj,obj.template); ajaxQueue('/?getlist=99',obj,obj,listdata); 很明显当第2个请求完成的时候,模板已经正确处理过了,可以进行视图的表现了。 事实上我上面的例子根本就无法在我上面提供的函数中正确运行,不好意思,那是最初的想法和代码,现在的写法如下 function ajaxQueue(data,apply,cb,args,ask){ if(typeof apply=='string' && !confirm(apply)) return; if(typeof cb=='string' && !confirm(cb)) return; if(typeof args=='string' && !confirm(args)) return; if(typeof ask=='string' && !confirm(ask)) return;//上面这些个判断都是用在缺少足够的参数,并且有提交提问的判断 if(typeof apply=='string') apply=false; var url,dat,type,datatype; if(typeof data=='string'){ url=data; dat=null; type='GET'; datatype='text'; }else{ url='/'; dat=$.toJSON(data); type='POST'; datatype='json'; } if (arguments.length) ajaxQueue.Queue.push({ url:url, data:dat, userData:{ cb:apply===false?null:(typeof cb=='function'?cb:arguments.callee.caller), apply:apply ||window, args:(args instanceof Array)?args:(cb instanceof Array?cb:[]) }, type:type, dataType:datatype, error:function(xhr){ if(xhr.responseText=='') alert('无效的请求.'); else alert('服务器数据错误.') setTimeout('ajaxQueue()',0); }, success: function(json){ thisCallback(this.userData.cb,this.userData.apply,[json].concat(this.userData.args)); setTimeout('ajaxQueue()',0); } }); var o=ajaxQueue.Queue.shift(); if(o) jQuery.ajax(o); } ajaxQueue.Queue=[]; function thisCallback(func,apply,args){ if (typeof func=='function'){ if (!(args instanceof Array)) args=args==undefined?[]:[args]; func.apply(apply,args); } return false; }
在上述写法中,ajax请求的数据data被自动的识别了。至于dat=$.toJSON(data) 的用法,参见我的另外一篇文章 降低前后台业务逻辑上的耦合度,前后台细粒度数据通讯的方法
|
|
返回顶楼 | |
发表时间:2009-05-08
谢谢你的详细答复
|
|
返回顶楼 | |
浏览 8877 次