2008年08月2日
[JavaScript] 五分鐘小教室 - 不重複送出 Ajax Request
這次在設計購物車時,遇到了以下的介面:

客戶的需求是在按下「 + 」 或「 - 」時,要以 Ajax 發送更新的數量到後端系統去驗算;每按一次「 + 」 或「 - 」,就要送出一次 Ajax Request。
可是這時候問題就來啦,如果數量要 10 個的話就要連續按 10 次「 + 」,也會連續發送 10 次的 Ajax Request ;這樣不但會浪費珍貴的網路頻寬,更不用說會造成後端系統的負擔。
怎麼解決呢?其實方法很多,而這裡我採用最簡單的 setTimeout 和 clearTimeout 。程式如下:
var sending = null;
var _formSubmit = function () {
alert('Form submited!');
};
var _doAjaxPost = function () {
if (sending !== null) {
clearTimeout(sending);
sending = null;
}
sending = setTimeout(_formSubmit, 1000);
};
var plusQuantity = function () {
// ... 執行增加數量的動作 ...
_doAjaxPost();
return false;
};
var minusQuantity = function () {
// ... 執行減少數量的動作 ...
_doAjaxPost();
return false;
};
$(function () {
// 增加數量
$('a.plus').click(plusQuantity);
// 減少數量
$('a.minus').click(minusQuantity);
});
註:這裡我大量使用了 jQuery 的功能。
想法很簡單,就是當我們按下「 + 」 或「 - 」時,要隔一秒才會送出 Ajax Request ;而在這一秒內如果再次按下「 + 」 或「 - 」,那麼就重新計時。
因此程式的主要重點在 _doAjaxPost 這個函式以及全域變數: sending ;當第一次呼叫 _doAjaxPost 時 sending 還是 null ,這時我們利用 setTimeout 開始計時,並將計時器指定給 sending 這個變數。而當第二次呼叫時, sending 變數已經不為空值,因此我們再利用 clearTimeout 將它清除,並重設為 null 以達到重新計時的目的。
是不是很簡單呢?
如果各位有更好的作法,也歡迎分享~
回應文章 
Posted by yoren
at 2008年08月3日 14:19
@yoren:
其實這裡在後端做的事遠比驗算複雜很多,只是因為我的重點不是驗算這件事,所以我略過很多細節。
Posted by jaceju
at 2008年08月3日 14:32
Dear Jace:
我是這樣做的:當圖片 onclick 時就先 _doAjaxPost(),然後再 unbind _doAjaxPost(),這樣的話,一直按下click 就會沒有用處 ,然後在接收 post 的那支 php 內使用 sleep(1) 的函式,這樣就會呆住一秒,然後在 js 收到 response 時,圖片再去 bind _doAjaxPost()。
大致上是這樣 ^^
我是這樣做的:當圖片 onclick 時就先 _doAjaxPost(),然後再 unbind _doAjaxPost(),這樣的話,一直按下click 就會沒有用處 ,然後在接收 post 的那支 php 內使用 sleep(1) 的函式,這樣就會呆住一秒,然後在 js 收到 response 時,圖片再去 bind _doAjaxPost()。
大致上是這樣 ^^
Posted by hermes
at 2008年08月4日 00:11
Hi hermest.
你如果 unbind _doAjaxPost() 的話
後端實在沒有必要再做 sleep 了
讓 client 收到 response 時 enable button 就夠了
Hi jaceju,
不過這篇的目的是否少解釋了使用者連續按的處理
應該是(在一秒內)連續按 n 下, 會先送一次+1, 後面n-1次會 queue 住, 等一秒後再送一次 n-1
否則如果使用者真的要買 10 個, 就要花十秒鐘, 這樣的設計是不合理的
你如果 unbind _doAjaxPost() 的話
後端實在沒有必要再做 sleep 了
讓 client 收到 response 時 enable button 就夠了
Hi jaceju,
不過這篇的目的是否少解釋了使用者連續按的處理
應該是(在一秒內)連續按 n 下, 會先送一次+1, 後面n-1次會 queue 住, 等一秒後再送一次 n-1
否則如果使用者真的要買 10 個, 就要花十秒鐘, 這樣的設計是不合理的
Posted by sy
at 2008年08月4日 01:43
Dear sy:
之所以會使用 sleep,主要的目的是在要求使用者 click 的間格必須要為一秒鐘。當然,之所以會這樣設計,主要是因為我是利用 db 來記錄 cart 內的商品資訊的緣故
不過您說的也沒有錯,某些情況底下若有利用 sleep 去限制使用者的 click 間格的確是不合理的 ^^
之所以會使用 sleep,主要的目的是在要求使用者 click 的間格必須要為一秒鐘。當然,之所以會這樣設計,主要是因為我是利用 db 來記錄 cart 內的商品資訊的緣故
不過您說的也沒有錯,某些情況底下若有利用 sleep 去限制使用者的 click 間格的確是不合理的 ^^
Posted by hermes
at 2008年08月4日 09:40
@sy:
耶?我應該一開始就有說是為了解決連續按的問題吧?還是你試過會 hang 住十秒?整個程式只有最後一次的 Request 會被送出,也是在一秒鐘後送出的。
目前程式運作還滿正常的,不曉得我哪裡有解釋不清的呢?請指教囉~
Posted by jaceju
at 2008年08月4日 09:50

@jaceyu:
我又重新看一次你的文章,才確定你的方法
你的方法是將使用者按的次數累積下來
直到按完之後(間隔超過一秒)才將總數一次送出, 是嗎?
那這樣應該沒什麼問題, 只是程式對於使用者的操作不是立即反應, 不過這問題不大
我原本是想成你限制每個 ajax request 必須間隔超過一秒,然後又是一個 request 代表 +1 的動作, 這樣下來如果要 +10, 就必須每隔一秒按一次, 整個流程必須花十秒.
我建議這個例子可以改成
var _formSubmit = function () {
alert('Form submited! ('+count+')');
count = 0;
};
並且在 plusQuantity()跟minusQuantity() 作 coun++ 及 count--
這樣會比較清楚..
Posted by sy
at 2008年08月4日 11:37
@sy:
哈!果然是例子的問題。因為我實在沒空想一下好範例,又不能直接拿線上實例來用 (很複雜) ,所以就簡單寫一下囉。
這邊有空我再改吧,被鬼抓去勞改中。
Posted by jaceju
at 2008年08月4日 11:42
Posted by tokimeki
at 2008年08月4日 11:55
@tokimeki:
簡單來說就是政治因素 ^^|||
而且就算是用下拉選單,也還是會遇到同樣的問題;因為有時候我們點選下拉選單後,不小心碰到滑鼠滾輪時,也會有連續同時送出 Request 的問題。
不過這裡政治因素的問題比較大,所以...
Posted by jaceju
at 2008年08月4日 12:16
