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); } }); /* 這種方法的唯一問題,就是在某些情況下--比如拖放式界面功能中--如果使用它,結果差不多會與傳統頁面重載的工作流一樣令人討厭。所以不能再腳本等待響應期間禁用拖動功能 */ // 增加資源占用