最近在做地圖和滾動條相關的功能:在綁定地圖的拖拽,縮放和滾動條的滾動事件時,遇到ajax隊列問題,即:當我把地圖拖到我想要的省市或者滾動條滾動到我想要的位置過程中,會發送很多ajax請求,而這些請求到的數據對用戶是無用的,只有最后一次才有用,並且還造成了很多服務器請求數的浪費。
然后在園子里找到了紅草帽 * Arain的這篇文章:多ajax請求的各類解決方案(同步, 隊列, cancel請求),研究了一下發現此方法好是好,但沒有解決http請求數的問題,因為就算是cancel掉請求,這個請求還是已經發送到了服務器(但並不絕對都是),並且當ajax執行比較快的時候cancel掉已經是無用了(當然實際場景中發送ajax間隔時間都是很短的,所以影響基本也很小)。
測試代碼:
for(var i=1;i<10;i++){
test(i);
}
function test(i){
setTimeout(function(){$.ajaxSingle($.extend(_settings,{
type:"post",
url:"PostPage.aspx?i="+new Date().getTime()+i,
data:"second=2",
anync:false,
className:"post"
}));},i*40);
}
測試結果:
可以看到間隔40ms發送請求就已經一大部分無法刪除掉了(雖然實際場景更多的是發送ajax請求間隔很短)。
然后就覺得寫那么多代碼已經沒必要了,簡單的用setTimeout實現就可以了,而且能從根上杜絕以上兩個問題發生。此方法也是在測試的時候發現的
var t; for(var i=1;i<10;i++){ clearTimeout(t); t=setTimeout(function(){ $.ajax({ type:"post", url:"PostPage.aspx", data:"second=1", success:function(msg){ }, error:function(){ } }); },100); }
這樣子做的話:就不會因為時間間隔,執行速度等原因造成的請求刪除不掉或者刪除掉但服務器已經執行部分程序的情況,代碼量少而且也很容易理解。
但是這也會有其他問題:比如用戶等待數據的時候可能會偏長,這時候就要根據實際發送ajax請求的間隔來合理定義延遲時間(通常都不會長)。
另外一些場景,比如表單的提交,這時候還是灰掉按鈕比較好。
以上是個人的一些想法,如方法有什么問題可以留言討論。
下面我把紅草帽 * Arain文章也貼出來,方便查看
-------------------------------------------------------------------------------------------------------------------------------------------------------
ajax帶來很好的用戶體驗,於是一個稍微注重web系統使用ajax基本成為必然。當傳統功能型web項目向用戶體驗型項目轉變時,層出不窮的需求就來了。正如本篇所介紹的就是一個多個AJAX請求的情況下,如何利用jquery來處理幾種case。
- 多個ajax請求同時發送,相互無依賴。
- 多個ajax請求相互依賴,必須有先后順序。
- 多個請求被同時發送,只需要最后一個請求。
第1種case
應用場景: 這個場景很多,一個頁面打開是多個區域同時請求后台得到各自的數據,沒依賴,沒順序。
處理方案: 直接用jquery的ajax函數。這個用的非常多,這里從略,可看后面的代碼中例子。
第2種case
應用場景: 多個ajax請求,需要順序執行,后一個ajax請求的執行參數是前一個ajax的結果。例如: 用戶登錄后我們發送一次請求得到用戶的應用ID,然后利用應用ID發送一次請求得到具體的應用內容(例子雖然不是太恰當,但基本就是這個意思了)。
處理方法:
1. 利用ajax參數async設置為false,進行同步操作。(這個方法只適合同域操作,跨域需使用下面兩種方法)
2. 利用ajax嵌套(這個同第1種情況)
3. 利用隊列進行操作
jquery ajax隊列操作核心代碼:
(function ($) {
var ajaxRequest = {};
$.ajaxQueue = function (settings) {
var options = $.extend({ className: 'DEFEARTNAME' }, $.ajaxSettings, settings);
var _complete = options.complete;
$.extend(options, {
complete: function () {
if (_complete)
_complete.apply(this, arguments);
if ($(document).queue(options.className).length > 0) {
$(document).dequeue(options.className);
} else {
ajaxRequest[options.className] = false;
}
}
});
$(document).queue(options.className, function () {
$.ajax(options);
});
if ($(document).queue(options.className).length == 1 && !ajaxRequest[options.className]) {
ajaxRequest[options.className] = true;
$(document).dequeue(options.className);
}
};
})(jQuery);
第3中case
應用場景: 比較典型的是autocomplete控件的操作,這個我們可以使用第2種情況的處理方法,但我們可能只需要最后次按鍵后返回的結果,這樣利用第2種處理方法未免有些浪費。
處理方法: 保留最后一次請求,cancel之前的請求。
(function ($) {
var jqXhr = {};
$.ajaxSingle = function (settings) {
var options = $.extend({ className: 'DEFEARTNAME' }, $.ajaxSettings, settings);
if (jqXhr[options.className]) {
jqXhr[options.className].abort();
}
jqXhr[options.className] = $.ajax(options);
};
})(jQuery);
對於這些case都是在多個ajax請求,響應時間不能控制的情況。下面是完整Demo代碼。
(function ($) {
var jqXhr = {},
ajaxRequest = {};
$.ajaxQueue = function (settings) {
var options = $.extend({ className: 'DEFEARTNAME' }, $.ajaxSettings, settings);
var _complete = options.complete;
$.extend(options, {
complete: function () {
if (_complete)
_complete.apply(this, arguments);
if ($(document).queue(options.className).length > 0) {
$(document).dequeue(options.className);
} else {
ajaxRequest[options.className] = false;
}
}
});
$(document).queue(options.className, function () {
$.ajax(options);
});
if ($(document).queue(options.className).length == 1 && !ajaxRequest[options.className]) {
ajaxRequest[options.className] = true;
$(document).dequeue(options.className);
}
};
$.ajaxSingle = function (settings) {
var options = $.extend({ className: 'DEFEARTNAME' }, $.ajaxSettings, settings);
if (jqXhr[options.className]) {
jqXhr[options.className].abort();
}
jqXhr[options.className] = $.ajax(options);
};
})(jQuery);
var ajaxSleep = (function () {
var _settings = {
type: 'GET',
cache: false,
success: function (msg) {
var thtml = $('#txtContainer').html();
$('#txtContainer').html(thtml + "<br />" + msg);
}
};
return {
get: function (seconds, mode, isAsync) {
var mode = mode || 'ajax',
isAsync = isAsync || false;
$[mode]($.extend(_settings, {
url: "ResponsePage.aspx?second=" + seconds,
async: isAsync,
className: 'GET'
}));
},
post: function (seconds, mode, isAsync) {
var mode = mode || 'ajax',
isAsync = isAsync || false;
$[mode]($.extend(_settings, {
type: 'POST',
url: "PostPage.aspx",
data: { second: seconds },
async: isAsync,
className: 'POST'
}));
}
};
} ());
var launch = function (settings) {
$('#txtContainer').html('');
var mode = settings.mode,
isAsync = settings.isAsync;
ajaxSleep.get(12, mode, isAsync);
ajaxSleep.get(10, mode, isAsync);
ajaxSleep.get(8, mode, isAsync);
ajaxSleep.post(6, mode, isAsync);
ajaxSleep.post(4, mode, isAsync);
ajaxSleep.post(2, mode, isAsync);
}
$(document).ready(function () {
//第1種case
$('#btnLaunchAsync').click(function () {
launch({ isAsync: true });
});
//第2種case
$('#btnLaunchSync').click(function () {
launch({});
});
//第2種case
$('#btnLaunchQueue').click(function () {
launch({ mode: 'ajaxQueue', isAsync: true });
});
//第3種case
$('#btnLaunchSingle').click(function () {
launch({ mode: 'ajaxSingle', isAsync: true });
});
});


后注: 個人能力有限,如有錯誤敬請指點。這些只是些根據一些特定情況下的處理,如果一個ajax請求能解決的問題切勿利用兩個請求來處理,畢竟需要占用資源。我還是相信沒有最好的方案,只有最適合的方案。