在比较复杂的模块中碰到无数次多个Ajax异步请求同时请求( 不会用direct),而且往往需要在这些请求都完成后,根据所有的请求结果做一些事情,比如提交表单什么的,最初的解决方式是一个请求的回调里嵌下一个请求,勉强实现后,调试维护异常纠心,一圈一圈像个洋葱。
接着右思左想之下,写下这个简易的还算能用的队列(我随便称呼的,各位大人轻拍)。
本队列能加普通函数、Ext.Ajax.request请求、Store请求,可以任意调整顺序(其实就一数组,所以,你们懂的),可以按顺序执行,也可以无序同时执行请求,在所有请求完成后有事件可监听 (废话太多了,上代码)
执行结果截图:(含一个Ajax请求、一个Store请求、一个普通函数,实例在代码的注释中,不过异步请求自己随意建就可以了)
队列顺序执行效果:
队列无序执行效果:
Ext.ns("Ext.ux.queue"); /** * @class Ext.ux.queue.Queue * @extends Ext.util.Observable * * 队列,支持Ajax,Store,普通Function * <pre><code> var log=[]; var queue = new Ext.ux.queue.Queue({ listeners:{ beforequeue: function(){ //Q.info("开始执行队列------------<br/>"); //return false; }, afterqueue: function(self, flag){ Q.info("完成---"+log.join("<br/>")+"<br/>"+flag); } } }); //-----------------------------------Ajax队列项--------------------------------- //1.直接使用【Ajax参数】方式创建Ajax队列项 var q1 = queue.addAjax({ url: path+"/sys/BusiUser^checkDiscountAuthorize.action", params:{authorizeUser: "smallBeautiful@4304", authorizePwd: "123000"}, success: function(response){ var json = Ext.decode(response.responseText); if(false === json.success){ log.push("执行【授权success】!---false"); return false; } log.push("执行【授权success】!---true"); //return true; }, failure: function(response){ log.push("执行【授权failure】!----false"); return false; }, callback: function(){ log.push("执行【授权callback】!"); //return true; } }); //2.直接【完整参数】方式创建Ajax队列项 var q1 = queue.addAjax({ params: { url: path+"/sys/BusiUser^checkDiscountAuthorize.action", params:{authorizeUser: "smallBeautiful@4304", authorizePwd: "123000"}, success: function(response){ var json = Ext.decode(response.responseText); if(false === json.success){ log.push("执行【授权success】!---false"); return false; } log.push("执行【授权success】!---true"); //return true; }, failure: function(response){ log.push("执行【授权failure】!----false"); return false; }, callback: function(){ log.push("执行【授权callback】!"); //return true; } }, listeners: { "beforeexecute": function(){ log.push("------------执行【q1 -> beforeexecute】!"); }, "afterexecute": function(){ log.push("------------执行【q1 -> afterexecute】!"); } } }); q1.on("beforeexecute", function(){ log.push("执行【q1 -> beforeexecute】!"); }); q1.on("afterexecute", function(){ log.push("执行【q1 -> afterexecute】!"); }); //-----------------------------------Ajax队列项----结束-------------------------- //-----------------------------------Store队列项--------------------------------- var q2 = queue.addStore({ store: grid.getStore(), params: { start:0, limit:10, callback: function(rs, options, success){ log.push("执行【store.load - callback】!"); //return success; } } //, //listeners: { // "beforeexecute": function(){ // log.push("执行【q2 -> beforeexecute】!"); // }, // "afterexecute": function(){ // log.push("执行【q222222 -> afterexecute】!"); // } //} }); q2.on("beforeexecute", function(){ log.push("执行【q2 -> beforeexecute】!"); //return false; }); q2.on("afterexecute", function(){ log.push("执行【q2 -> afterexecute】!"); }); //两种移除队列方式 queue.remove(q2); //queue.removeAt(0); //-----------------------------------Store队列项----结束------------------------- //-----------------------------------普通函数队列项------------------------------ //1.使用【函数参数】创建函数队列项 //var q3 = queue.addFn(function(){ // log.push("执行【q3 - fn】!"); // return true; //返回false,则代表当前队列项执行失败 //}); //2.使用【完整参数】创建函数队列项 var q3 = queue.addFn({ fn: function(json){ log.push("执行【q3 - fn】!-----"+json.aaa); return true; }, params:{aaa: "我是函数参数"}, listeners: { "beforeexecute": function(){ log.push("----listeners----执行【q3 -> beforeexecute】!"); }, "afterexecute": function(){ log.push("----listeners----执行【q3 -> afterexecute】!"); } } }); q3.on("beforeexecute", function(){ log.push("执行【q3 -> beforeexecute】!"); }); q3.on("afterexecute", function(){ log.push("执行【q3 -> afterexecute】!"); }); //【注】:可以在添加队列项时,设置顺序:queue.addAjax({...}, 1); //将当前添加的队列项设置为队列的第2项 queue.orderExecute(); //顺序执行队列 //queue.execute(); //无序执行队列 </code></pre> * @author tipx * @homepage http://tipx.iteye.com * @version 0.1 * @revision $Id: Ext.ux.queue.js 5 2011-04-14 10:35:16 tipx $ * @depends Q * * @license Ext.ux.queue.Queue is licensed under the terms of * the Open Source LGPL 3.0 license. Commercial use is permitted to the extent * that the code/component(s) do NOT become part of another Open Source or Commercially * licensed development library or toolkit without explicit permission. * * <p>License details: <a href="http://www.gnu.org/licenses/lgpl.html" * target="_blank">http://www.gnu.org/licenses/lgpl.html</a></p> */ Ext.ux.queue.Queue = Ext.extend(Ext.util.Observable, { //items:[], //队列 //disorderCount:0, //队列完成计数器,初始设置为当前items.length(防止在请求途中有添加item),每执行一个请求该值减1,值为0时代表所有执行完毕,用于无序请求时,触发afterqueue事件 //orderCount:0, //队列执行计数器,初始为当前items.length,每执行一个请求该值减1,值为0时代表所有执行完毕 constructor: function(cfg){ this.items=[]; //队列容器 Ext.apply(this, cfg); this.initEvents(); Ext.ux.queue.Queue.superclass.constructor.call(this, cfg); }, //添加事件 initEvents: function(){ this.addEvents( /** * @event beforequeue 队列开始执行前事件<br/>若事件返回false,则终止执行队列 * @param {Ext.ux.queue.Queue} this 当前队列 */ "beforequeue", /** * @event afterqueue 队列开始执行完成事件 * @param {Ext.ux.queue.Queue} this 当前队列 * @param {boolean} success 队列执行结果,true为成功,false为失败 * <br/>当队列中任意一个队列项执行结果为false,则队列执行结果为false * <br/>若当前队列为顺序执行时,某一队列项执行结果为false,则终止队列执行,队列执行结果为false */ "afterqueue", /** * @event execute 任意一个队列项执行完毕 * @param {Ext.ux.queue.Queue} this 当前队列 * @param {boolean} success 队列项执行结果,true为成功,false为失败 */ "execute" ); //监听队列项执行完毕事件 this.on("execute", function(self, success){ //Ext.ux.queue.EXECUTETYPE.DISORDER : 无序执行 //Ext.ux.queue.EXECUTETYPE.ORDER : 有序执行 this[this.executeType+"ExecuteItem"](success); //根据执行类型,执行相应的方法 }); //队列执行完成后,重置队列执行类型 this.on("afterqueue", function(){ this.executeType = null; }); }, /** * 获取队列长度 * @return {int} 队列长度 */ getCount: function(){ return this.items.length; }, /** * 根据索引获取队列项 * @param {int} index 索引 * @return {Ext.ux.queue.QueueItem} 队列项 */ getAt: function(index){ return this.items[index]; }, /** * 将普通Ajax请求添加到队列 * @param {object} json 配置信息;允许直接传入Ajax请求参数 * @param {int} index (Optional) 队列索引,通过该参数可调整当前队列项所处的顺序,缺省添加到队列当前最后一位 * @return {Ext.ux.queue.QueueItem} 新添加的队列项 */ addAjax : function(json, index){ //url存在时,当前整个作为request的params if(json.url){ json = { params: json }; } json.qtype = Ext.ux.queue.QTYPE.AJAX; return this.addItem(json, index); }, /** * 将Store请求添加到队列 * @param {object} json 配置信息 * @param {int} index (Optional) 队列索引,通过该参数可调整当前队列项所处的顺序,缺省添加到队列当前最后一位 * @return {Ext.ux.queue.QueueItem} 新添加的队列项 */ addStore : function(json, index){ return this.addItem(Ext.apply({ qtype: Ext.ux.queue.QTYPE.STORE }, json), index); }, /** * 添加普通函数添加到队列 * @param {object/function} json 配置信息,直接传入函数则作为队列执行项 * @param {int} index (Optional) 队列索引,通过该参数可调整当前队列项所处的顺序,缺省添加到队列当前最后一位 * @return {Ext.ux.queue.QueueItem} 新添加的队列项 */ addFn: function(json, index){ //当json参数是函数时,则自动转换 if(Ext.isFunction(json)){ json = { fn: json }; } json.qtype = Ext.ux.queue.QTYPE.FUNCTION; return this.addItem(json, index); }, /** * 添加队列项(对象) * @param {Ext.ux.queue.QueueItem} item 队列项对象 * @param {int} index (Optional) 队列索引,通过该参数可调整当前队列项所处的顺序,缺省添加到队列当前最后一位 * @return {Ext.ux.queue.QueueItem} 新添加的队列项 */ add: function(item, index){ this.items.splice(this.createIndex(index), 0, item); return item; }, //private addItem: function(json, index){ var item = new Ext.ux.queue.QueueItem(json); this.items.splice(this.createIndex(index), 0, item); return item; }, /** * 移除队列项 * @param {Ext.ux.queue.QueueItem} item 待移除的队列项 * @return {Ext.ux.queue.QueueItem} 被移除的队列项;若未在当前队列中找到该队列项,将返回undefined */ remove: function(item){ var index = this.items.indexOf(item); if(index < 0){return item;} return this.removeAt(index); }, /** * 按index移除队列项 * @param {int} index 待移除的队列项索引 * @return {Ext.ux.queue.QueueItem} 被移除的队列项;若未在当前队列中找到该队列项,将返回undefined */ removeAt: function(index){ var item = this.items[index]; this.items.splice(index, 1); return item; }, //private //根据index生成有效的index值 createIndex: function(index){ var len = this.items.length; //不能直接使用 index = index || len; //此种方式导致index为0时,将会使用len的值 if(Ext.isEmpty(index)){ index = len; } index = Math.min(index, len); return index; }, /** * 不限制顺序地执行队列 */ execute: function(){ if(false === this.fireEvent("beforequeue", this)){ return; } var items = this.items; this.disorderState = true; //初始执行状态,当无序队列中有一个队列项执行结果为false,则队列执行结果为false this.executeType = Ext.ux.queue.EXECUTETYPE.DISORDER; //设置执行类型 this.disorderCount = items.length; //初始化【无序队列】计数器 if(items.length < 1){ this.fireEvent("afterqueue", this, true); } var thiz = this; Q.each(this.items, function(item){ thiz.executeItem(item); //执行队列 }); }, /** * 按顺序执行队列 */ orderExecute: function(){ if(false === this.fireEvent("beforequeue", this)){ return; } var items = this.items; this.executeType = Ext.ux.queue.EXECUTETYPE.ORDER; //设置执行类型 this.orderCount = 0; //初始化【有序队列】计数器 if(items.length < 1){ this.fireEvent("afterqueue", this, true); } this.executeItem(items[0]); //开始执行队列 }, //private //执行队列项 executeItem: function(item){ var thiz = this; //队列项执行完毕事件监听 item.on("afterexecute", function(item, flag){ thiz.fireEvent("execute", this, false !== flag); }, item, {single:true}); item.execute(); }, //private //顺序执行队列项 orderExecuteItem: function(success){ this.orderCount++; var item = this.getAt(this.orderCount); if(item){ if(false === success){ //执行失败时触发队列完成事件 this.fireEvent("afterqueue", this, false); }else{ //执行成功则继续往下执行 this.executeItem(item); //开始执行队列 } }else{ //最后的队列项 //执行成功或失败都触发队列完成事件 this.fireEvent("afterqueue", this, false !== success); } }, //private //无序执行队列项 disorderExecuteItem: function(success){ this.disorderCount--; //当无序队列中有一个队列项执行结果为false,则队列执行结果为false if(false === success){ this.disorderState = false; } //为0时,执行完毕 if(this.disorderCount == 0){ this.fireEvent("afterqueue", this, this.disorderState); } } }); //private Ext.apply(Ext.ux.queue, { /** * @class Ext.ux.queue.QTYPE * 队列项类型 */ QTYPE : { /** * 队列项函数类型 * 直接使用返回值来判断执行结束,触发执行完成事件,并获得执行结果 * @type Ext.ux.queue.QTYPE * @property FUNCTION */ FUNCTION: "Fn", /** * 队列项Ajax类型 * @type Ext.ux.queue.QTYPE * @property AJAX */ //普通ajax请求,如: Ext.Ajax.request,只需要传用于request的参数(url、params、callback等)即可 //通过添加回调函数来感知执行结束、触发执行完成事件,以及获取执行结果 AJAX: "Ajax", /** * 队列项Store类型 * @type Ext.ux.queue.QTYPE * @property STORE */ //数据源请求, 如:store.load, store.reload,需要分开传入store对象,以及参数(参数应当允许为空) //通过添加回调函数来感知执行结束、触发执行完成事件,以及获取执行结果 STORE: "Store" }, //执行类型 EXECUTETYPE: { DISORDER: "disorder", //无序执行 ORDER: "order" //有序 } }); /** * @class Ext.ux.queue.QueueItem * @extends Ext.util.Observable * * 队列项,支持Ajax,Store,普通Function * <pre><code> var qi = new Ext.ux.queue.QueueItem({ qtype: Ext.ux.queue.QTYPE.FUNCTION, fn: function(json){ log.push("执行【qi - fn】!-----"+json.ccc); return true; }, params: {ccc: "item参数!!!"}, listeners: { "beforeexecute": function(){ log.push("listeners----执行【qi -> beforeexecute】!"); }, "afterexecute": function(){ log.push("listeners----执行【qi -> afterexecute】!"); } } }); qi.execute(); //单独执行队列项 //将队列项放入队列中执行 var queue = new Ext.ux.queue.Queue(); queue.add(qi); queue.execute(); </code></pre> * @author tipx * @homepage http://tipx.iteye.com * @version 1 * @revision $Id: Ext.ux.queue.js 5 2011-04-14 11:06:25 tipx $ * @depends Q * * @license Ext.ux.queue.QueueItem is licensed under the terms of * the Open Source LGPL 3.0 license. Commercial use is permitted to the extent * that the code/component(s) do NOT become part of another Open Source or Commercially * licensed development library or toolkit without explicit permission. * * <p>License details: <a href="http://www.gnu.org/licenses/lgpl.html" * target="_blank">http://www.gnu.org/licenses/lgpl.html</a></p> */ Ext.ux.queue.QueueItem = Ext.extend(Ext.util.Observable, { constructor: function(cfg){ cfg = Ext.apply({ /** * @cfg {function} fn 普通函数队列项中的执行函数<br/>函数执行结果为false,则事件afterexecute的success参数为false */ //fn: undefined, //ajax: undefined, /** * @cfg {Ext.data.Store} store store函数队列项时,必传属性<br/>该回调函数执行结果为false,则事件afterexecute的success参数为false */ //store: undefined, /** * @cfg {object} scope 执行函数、回调函数中使用的this代表的作用域,缺省为当前队列项QueueItem */ scope: this, //作用域,缺省为当前队列项 /** * @cfg {Ext.ux.queue.QTYPE} qtype 队列项类型 * <br/>(缺省值)普通函数队列项:Ext.ux.queue.QTYPE.FUNCTION * <br/>Ajax队列项:Ext.ux.queue.QTYPE.AJAX * <br/>Store队列项:Ext.ux.queue.QTYPE.STORE */ qtype: Ext.ux.queue.QTYPE.FUNCTION, //类型缺省为普通函数 /** * @cfg {object} params 执行参数<br/> * 普通函数队列项时:该参数为执行函数的参数,参数只能使用一个json格式的参数;<br/> * Ajax队列项时:该参数为执行Ajax请求的参数;<br/> * Store队列项时:该参数为Store.load方法的参数;<br/> * 【注】:异步请求时,回调函数执行结果为false,则事件afterexecute的success参数为false */ params:{} //执行参数,缺省为空 }, cfg); Ext.apply(this, cfg); this.initEvents(); Ext.ux.queue.QueueItem.superclass.constructor.call(this, cfg); }, //添加事件 initEvents: function(){ this.addEvents( /** * @event beforeexecute 队列项执行前触发<br/>若事件返回false,则中止执行队列项 * @param {Ext.ux.queue.QueueItem} this 当前队列项 */ "beforeexecute", /** * @event afterexecute 队列项执行后触发 * @param {Ext.ux.queue.QueueItem} this 当前队列项 * @param {boolean} success 当前队列项执行结果,true为成功,false为失败 */ "afterexecute" ); }, //private //执行函数 execFn: function(){ var flag = (false !== this.fn.call(this.scope, this.params)); this.fireEvent("afterexecute", this, flag); }, //private //执行Ajax execAjax: function(){ var thiz = this, scope = this.scope; var params = Ext.ux.util.clone(this.params), callbackFn = params.callback, successFn = params.success, failureFn = params.failure; //callback不需要删除,因为会被生成的callback覆盖 delete params.success; //从参数中删除,避免重复执行 delete params.failure; //从参数中删除,避免重复执行 //将params中的回调函数统一归整到callback中 var callback = function(options, success, response){ var flag = success; //初始设置为success //若回调函数存在,则执行它,并取出结果 if(callbackFn){ flag = callbackFn.call(scope, options, success, response); } //如果有successFn/failureFn,则将忽略callbackFn的执行结果 if(success){ if(successFn){ flag = successFn.call(scope, response, options); } }else{ if(failureFn){ flag = failureFn.call(scope, response, options); } } //触发执行完成事件 thiz.fireEvent("afterexecute", this, flag); } params.callback = callback; Ext.Ajax.request(params); }, //private //执行Store execStore: function(){ var thiz = this, scope = this.scope; var store = this.store, params = this.params, callbackFn = params.callback; //将params中的回调函数统一归整到callback中 var callback = function(rs, options, success){ var flag = success; //初始设置为success //若回调函数存在,则执行它,并取出结果 if(callbackFn){ flag = callbackFn.call(scope, rs, options, success); } //触发执行完成事件 thiz.fireEvent("afterexecute", this, flag); } params.callback = callback; store.load(params); }, /** * 队列项执行 */ execute: function(){ if(false === this.fireEvent("beforeexecute", this)){ return; } //根据类型,取出相应的方法执行 this["exec"+this.qtype](); } });
【注意】:
1.本代码虽然是一个独立的js,但引用了两个插件:Q和Ext.ux.util,直接将Q.each替换成Ext.each或自己的each方法即可以;Ext.ux.util是国外ext大牛站上下载的,见附件。
2.代码上注释巨多是在ext-doc生成API时使用的,我就不一一删除了,有些注释的内容就是例子
1 楼
tipx
2011-04-14
iteye的域名真不习惯。。。