解决一次要发出多个ajax请求
jquery ajax队列管理插件
不错的ajax的jquery队列管理器
;(function($) { function AjaxQueue(override) { this.override = !!override; }; AjaxQueue.prototype = { requests: new Array(), offer: function(options) { var _self = this; var xhrOptions = $.extend({}, options, { complete: function(jqXHR, textStatus) { if($.isArray(options.complete)) { var funcs = options.complete; for(var i = 0, len = funcs.length; i < len; i++) funcs[i].call(this, jqXHR, textStatus); } else { if(options.complete) options.complete.call(this, jqXHR, textStatus); } _self.poll(); }, beforeSend: function(jqXHR, settings) { if(options.beforeSend) var ret = options.beforeSend.call(this, jqXHR, settings); if(ret === false) { _self.poll(); return ret; } } }); if(this.override) { this.replace(xhrOptions); } else { this.requests.push(xhrOptions); if(this.requests.length == 1) { $.ajax(xhrOptions); } } }, replace: function(xhrOptions) { var prevRet = this.peek(); if(prevRet != null) { prevRet.abort(); } this.requests.shift(); this.requests.push($.ajax(xhrOptions)); }, poll: function() { if(this.isEmpty()) { return null; } var processedRequest = this.requests.shift(); var nextRequest = this.peek(); if(nextRequest != null) { $.ajax(nextRequest); } return processedRequest; }, peek: function() { if(this.isEmpty()) { return null; } var nextRequest = this.requests[0]; return nextRequest; }, isEmpty: function() { return this.requests.length == 0; } }; var queue = {}; var AjaxManager = { createQueue: function(name, override) { return queue[name] = new AjaxQueue(override); }, destroyQueue: function(name) { if(queue[name]) { queue[name] = null; delete queue[name]; } }, getQueue: function(name) { return ( queue[name] ? queue[name] : null); } }; $.AM = AjaxManager; })(jQuery); 使用示例: var newQueue = $.AM.createQueue('queue'); $(function(){ newQueue.offer({url:'?c=Message&m=write&a=10'}); newQueue.offer({url:'?c=Message&m=write&a=10'}); newQueue.offer({url:'?c=Message&m=write&a=1'}); });
第二个插件
(function($) { var ajax = $.ajax, pendingRequests = {}, synced = [], syncedData = [], ajaxRunning = []; $.ajax = function(settings) { // create settings for compatibility with ajaxSetup settings = jQuery.extend(settings, jQuery.extend({}, jQuery.ajaxSettings, settings)); var port = settings.port; switch (settings.mode) { case "abort": if (pendingRequests[port]) { pendingRequests[port].abort(); } return pendingRequests[port] = ajax.apply(this, arguments); case "queue": var _old = settings.complete; settings.complete = function() { if (_old) { _old.apply(this, arguments); } if (jQuery([ajax]).queue("ajax" + port).length > 0) { jQuery([ajax]).dequeue("ajax" + port); } else { ajaxRunning[port] = false; } }; jQuery([ajax]).queue("ajax" + port, function() { ajax(settings); }); if (jQuery([ajax]).queue("ajax" + port).length == 1 && !ajaxRunning[port]) { ajaxRunning[port] = true; jQuery([ajax]).dequeue("ajax" + port); } return; case "sync": var pos = synced.length; synced[pos] = { error: settings.error, success: settings.success, complete: settings.complete, done: false }; syncedData[pos] = { error: [], success: [], complete: [] }; settings.error = function() { syncedData[pos].error = arguments; }; settings.success = function() { syncedData[pos].success = arguments; }; settings.complete = function() { syncedData[pos].complete = arguments; synced[pos].done = true; if (pos == 0 || !synced[pos - 1]) for (var i = pos; i < synced.length && synced[i].done; i++) { if (synced[i].error) synced[i].error.apply(jQuery, syncedData[i].error); if (synced[i].success) synced[i].success.apply(jQuery, syncedData[i].success); if (synced[i].complete) synced[i].complete.apply(jQuery, syncedData[i].complete); synced[i] = null; syncedData[i] = null; } }; } return ajax.apply(this, arguments); }; })(jQuery); (function(){ $("body").queue([]); $("#dtitle").click(function(){ $.ajax({ url: "test.php?t=" + new Date().getMilliseconds(), success: function(html){ jQuery("ul").append(html); }, //用abort而不用queue,是因为需求是需要最后一个ajax request,而之前的ajax request //其实并没有用,那何必要等它们执行完呢?中途就可以把它中止掉 mode: "abort" }); }); });
第三各ajax高级程序设计上的。
/* * 将ajax根据优先级进行排列的方法 * 构造一个简单的排列函数,接受一个排序的函数 * 所有添加的ajax保存到_items中 * */ function PriorityQueue(fn){ this._items = []; if(typeof fn == 'function'){ this._compare = fn; } } PriorityQueue.prototype = { constructor:PriorityQueue, _compare:function(oValue1,oVlaue2){ if(oValue1<oVlaue2){ return -1; }else if(oValue1 > oVlaue2){ return 1; }else{ return 0; } }, //排序 prioritze:function(){ this._items.sort(this._compare) }, //移除并返回第一个ajax get:function(){ return this._items.shift(); }, //返回对列中指定的ajax item:function(iPos){ return this._items[iPos]; }, //返回队列中第一个ajax peek:function(){ return this._items[0]; }, //将一个ajax插入到队列中,并排序 put:function(oValue){ this._items.push(oValue); this.prioritze(); }, //返回队列的长度 size:function(){ return this._items.length; }, //移除一个指定的ajax,成功后返回true,否则false remove:function(oValue){ for(var i=0,len=this._items.length;i<len;i++){ if(this._items[i] == oValue){ this._items.splice(i,1); return true; } }; return false; } } /* * * */ var RequestManager = (function(){ var oManager = { //队列是最长等待时间 AGE_LIMIT:60000, //默认优先级10 DEFAULT_PRIORTY:10, //检查队列时间间隔 INTERVAL:250, //保存正在执行的ajax _active:[], //队列实例 _pending:new PriorityQueue(function(oRequest1,oRequest2){ return oRequest1.priority - oRequest2.priority; }), //检查每个ajax的等待时间,如果超出默认的最长时间,则提高该ajax的优先级 _agePromote:function(){ for(var i=0;i<this._pending.size();i++){ var oRequest = this._pending._items[i]; oRequest.age += this.INTERVAL; if(oRequest.age >= this.AGE_LIMIT){ oRequest.age = 0; oRequest.priority--; }; }; this._pending.prioritze(); }, //检查正在执行的ajax状态, _checkActiveRequests:function(){ var oRequest = null; var oTransport = null; for(var i=this._active.length-1; i>=0; i--){ oRequest = this._active[i]; oTransport = oRequest.transport; if(oTransport.readyState == 4){ oRequest.active = false; this._active.splice(i,1); var fnCallback = null; if(oTransport.status >= 200 && oTransport.status < 300){ if(typeof oRequest.onsuccess == 'function'){ fnCallback = oRequest.onsuccess; } }else if(oTransport.status == 304){ if(typeof oRequest.onnotmodified == 'function'){ fnCallback = oRequest.onnotmodified; } }else{ if(typeof oRequest.onfailure == 'function'){ fnCallback = oRequest.onfailure; } } if(fnCallback != null){ setTimeout((function(fnCallback,oRequest,oTransport){ return function(){ fnCallback.call(oRequest.scope||window, { status : oTransport.status, data : oTransport.responseText, request : oRequest }) } })(fnCallback,oRequest,oTransport),1); } } } }, //封装XMLHttpRequest _createTransprot:function(){ if(typeof XMLHttpRequest != 'undefined'){ return new XMLHttpRequest(); }else if(typeof ActiveXObject != 'undefined'){ var xhr = null; try{ xhr = new ActiveXObject('MSXML2.XmlHttp.6.0'); return xhr; }catch(e){ try{ xhr = new ActiveXObject('MSXML2.XmlHttp.3.0'); return xhr; }catch(e){ throw Error('cannot create XMLHttp object!'); } } } }, //发送一下个请求,检查当前执行的ajax是否小于2,如果是,则激活下一个ajax _sendNext:function(){ if(this._active.length <2){ var oRequest = this._pending.get(); if(oRequest != null){ this._active.push(oRequest); oRequest.transport = this._createTransprot(); oRequest.transport.open(oRequest.type,oRequest.url,true); oRequest.transport.send(oRequest.data); oRequest.active = true; } } }, //取消指定的ajax,如果有回调函数oncancel,则执行 cancel:function(oRequest){ if(!this._pending.remove(oRequest)){ oRequest.transport.abort(); if(this._active[0] === oRequest){ this._active.shift(); }else if(this._active[1] === oRequest){ this._active.pop(); }; if(typeof oRequest.oncancel == 'function'){ oRequest.oncancel.call(oRequest.scope||window,{request:oRequest}) } } }, //添加一个ajax到队列中 send:function(oRequest){ if(typeof oRequest.priority != 'number'){ oRequest.priority = this.DEFAULT_PRIORTY; }; oRequest.active = false; oRequest.age = 0; this._pending.put(oRequest); }, /* * 预置一些方面,方便不知道该如何设置优先的情况 * 其实也就是给这些方法加了个默认的优先级 */ poll:function(oRequest){ oRequest.priority = 3; this.send(oRequest); }, prefetch:function(oRequest){ oRequest.priority = 5; this.send(oRequest); }, submit:function(oRequest){ oRequest.priority = 0; this.send(oRequest); }, submitPart:function(oRequest){ oRequest.priority = 2; this.send(oRequest); }, }; //通过setInterval,不断的检查队列中ajax执行情况, //如果执行完,添加下一个 //如果超过最长等待时间,则提高优先级 //据说这里之所以不用onreadystatechange是为了避免IE下的内存问题 //但感觉这样在页面上不停的setinterval,同样让人蛋疼啊! setInterval(function(){ RequestManager._checkActiveRequests(); RequestManager._sendNext(); RequestManager._agePromote(); },oManager.INTERVAL); return oManager; })(); /* 用法: RequestManager.send({ priority:0, type:'get', url:'data.txt', onsuccess:function(){}, onfailure:function(){}, onnotmodified:function(){} }) */
第四:
/* 完成请求的赛跑 异步请求几乎同时发生,但却不会同步而且次序经常颠倒。 1.延迟决定胜利者 在赛跑过程中,无论是服务器还是脚本都无法使某个请求更快地得到响应。请求过程中的延迟会出现在几个阶段,而其中的多个阶段都是你无法控制的。 所有通信过程都将遵循如下相同的模式: (1)客户端计算机对服务器发起获取或修改信息的请求。 (2)将请求通过一条计算机网络发送到服务器。 (3)服务器处理请求。 (4)服务器将对请求的响应通过另一条计算机网络发送回客户端计算机 在这个请求/相应循环的过程中,每个阶段都存在外部因素的影响。 2.处理异步请求 处理请求/响应循环中的延迟问题有很多不同的方式。以下是其中几种主要的思路: (1)置之不理。 (2)关掉异步行为。 在Ajax对象中设置asynchronous=false是另外一种选择,但这个选择也可以从你的方案清单中划掉。如果在XMLHttpRequest对象上设置了同步模式,那么它会按照次序处理请求,但它是通过把请求转换为一种更加激进的阻塞模式来实现这一点的。在这种情况下,你的脚本将被迫停止运行直至请求完成,期间可能会因为响应过慢而导致脚本和浏览器被挂起。 3.在客户端队请求排队 排队是另一种可能的方案。与其一次发送多个XMLHttpRequest请求,不如等到前一个请求获得相应后再发送下一个。 */ /* 一个复制对象的辅助方法 */ function clone(myObj) { if (typeof myObj !== 'object') { return myObj; } if (myObj === null) { return myObj; } var myNewObj = {}; for (var i in myObj) { myNewObj[i] = clone(myObj[i]); } return myNewObj; } /* 用于保存队列的数组 */ var requestQueue = []; /** * 为ADS.ajaxRequest方法启用排队功能的包装对象 * @param url * @param options * @param queue * @example * ADS.ajaxRequestQueue('/your/script/', { * completeListener: function(){ * alert(this.responseText); * } * }, 'Queue1'); */ function ajaxRequestQueue(url, options, queue) { queue = queue || 'default'; // 这个对象将把可选的侦听器包装在另一个函数中 // 因此,可选的对象必须唯一。否则,如果该方法 // 被调用时使用的是共享的可选对象,那么会导致 // 陷入递归中 options = clone(options) || {}; if (!requestQueue[queue]) { requestQueue[queue] = []; } // 当前一次请求完成时,需要使用completeListener // 调用队列中的下一次请求。如果完成侦听器已经 // 有定义,那么需要首先调用它 // 取得旧侦听器 var userCompleteListener = options.completeListener; // 添加新侦听器 options.completeListener = function () { // 如果存在旧的侦听器则首先调用它 if (userCompleteListener) { // this引用的是情求对象 userCompleteListener.apply(this, arguments); } // 从队列中移除这个请求 requestQueue[queue].shift(); // 调用队列中的下一项 if (requestQueue[queue][0]) { // 请求保存在req属性中,但为防止它是 // 一个POST请求,故也需包含send选项 var q = requestQueue[queue][0].req.send( requestQueue[queue][0].send ); } }; // 如果发生了错误,应该通过调用相应的 // 错误处理方法取消队列中的其他请求 // 取得旧侦听器 var userErrorListener = options.errorListener; // 添加新侦听器 options.errorListener = function () { if (userErrorListener) { userErrorListener.apply(this, arguments); } // 由于已经调用了错误侦听器 // 股从队列中移除这个请求 requestQueue[queue].shift(); // 由于出错需要取消队列中的其余请求,但首先要调用 // 每个请求的errorListener。通过调用队列中 // 下一项的错误侦听器就会才清楚所有排队的请求,因为在 // 链中的调研那个是一次发生的 // 检测队列中是否还存在请求 if (requestQueue[queue].length) { // 取得下一项 var q = requestQueue[queue].shift(); // 中断请求 q.req.abort(); // 伪造请求对象,以便errorListener // 认为请求已经完成并相应地运行 var fakeRequest = {}; // 将status设置为0,将readyState设置为4 // 就好像请求虽然完成但却失败了一样 fakeRequest.status = 0; fakeRequest.readyState = 4; fakeRequest.responseText = null; fakeRequest.responseXML = null; // 设置错误信息,以便需要时显示 fakeRequest.statusText = 'A request in the queue received an error'; // 调用状态改变,如果readyState是4,而 // status不是200,则会调用errorListener q.error.apply(fakeRequest); } }; // 将这个请求添加到队列中 requestQueue[queue].push({ req: getRequestObject(url, options), send: options.send, error: options.errorListener }); // 如果队列的长度表明只有一个 // 项(即第一个)则调用请求 if (requestQueue[queue].length === 1) { ajaxRequest(url, options); } } window.ADS.ajaxRequestQueue = ajaxRequestQueue; //队列中的请求1 ADS.ajaxRequestQueue('/your/script/', { completeListener: function () { alert(this.responseText); } }, 'Queue1'); //队列中的请求2 ADS.ajaxRequestQueue('/your/script/', { completeListener: function () { alert(this.responseText); } }, 'Queue2'); //队列1中的请求1,要等到请求1完成 ADS.ajaxRequestQueue('/your/script/', { completeListener: function () { alert(this.responseText); } }, 'Queue1'); // 队列1与队列2会在同一时刻以异步方式运行 /* 4.令请求异步但禁用有冲突的功能 禁用功能可能是避免不协调问题的最常见方法了。当执行某些异步请求时,让用户知道后台在干什么永远是很重要的。而这通常是通过在请求等待响应期间显示“载入中”等信息或者动画来完成。在等待期间,用户可能会犹豫急不可耐而在载入完成之前有执行了相同的操作,那么就可能对程序造成潜在的破坏。 除了显示简单的“载入中”信息之外,还可以禁用程序中的某个部件,以防止用户在不耐烦的情况下重复操作。而实现这一点的唯一技巧,就是无论是响应成功,还是发生了错误都要重新启用所禁用的部件。 */ // 例如:Web应用程序中包含如下提交按钮 // <input type="submit" id="buttonID"> // 那么,就可以禁用表单提交功能 ADS.ajaxRequest('/your/script/', { loadListener: function () { // 在载入期间禁用按钮 ADS.$('buttonID').disabled = 'disabled'; }, completeListener: function () { // 当响应成功时启用按钮 ADS.$('buttonID').disabled = ''; alert(this.responseText); }, errorListener: function () { // 当发生错误时也要启用按钮 ADS.$('buttonID').disabled = ''; alert('Oops, please try again:' + this.statusText); } }); /* 这种方法的唯一问题,就是在某些情况下--比如拖放式界面功能中--如果使用它,结果差不多会与传统页面重载的工作流一样令人讨厌。所以不能再脚本等待响应期间禁用拖动功能 */ // 增加资源占用