怎樣防止重複發送 Ajax 請求?

一個簡易的需求,點一個按鈕,則向服務器請求資源,不做處理時,屢次點擊後會有不少個請求在等待。粗暴的解決方式是點一次就將按鈕disable掉。請問一下有沒有更好的辦法,好比多點一次後自動down掉前一次請求?補充:不是一次請求,更相似於gmail的全站AJAX,剛用firebug看了一下gmail,發現重複請求時,以前的請求狀態變爲「Aborted」,而且不反回任何數據。請問是如何作到的呢?畢竟通常理解客戶端AJAX發送後是不能終止的。git

 

 

A. 獨佔提交
只容許同時存在一次提交操做,而且直到本次提交完成才能進行下一次提交。api

module.submit = function() { if (this.promise_.state() === 'pending') { return } return this.promise_ = $.post('/api/save') } 


B. 貪婪提交
無限制的提交,可是以最後一次操做爲準;亦即須要儘快給出最後一次操做的反饋,而前面的操做結果並不重要。promise

module.submit = function() { if (this.promise_.state() === 'pending') { this.promise_.abort() } // todo } 

好比某些應用的條目中,有一些進行相似「喜歡」或「不喜歡」操做的二態按鈕。若是按下後不當即給出反饋,用戶的目光焦點就可能在那個按鈕上停頓許久;若是按下時即時切換按鈕的狀態,再在程序上用 abort 來實現積極的提交,這樣既能提升用戶體驗,還能下降服務器壓力,皆大歡喜。

C. 節制型提交
不管提交如何頻繁,任意兩次有效提交的間隔時間一定會大於或等於某一時間間隔;即以必定頻率提交。服務器

module.submit = throttle(150, function() { // todo }) 

若是客戶發送每隔100毫秒發送過來10次請求,此模塊將只接收其中6個(每一個在時間線上距離爲150毫秒)進行處理。
這也是解決查詢衝突的一種可選手段,好比以知乎草稿舉例,仔細觀察能夠發現:
編輯器的 blur 事件會當即觸發保存;
保存按鈕的 click 事件也會當即觸發保存;
可是存在一種狀況會使這兩個事件在數毫秒內連續發生——當焦點在編輯器內部,而且直接去點擊保存按鈕——這時用 throttle 來處理是可行的。
另外還有一些事件處理會很頻繁地使用 throttle,如: resize、scroll、mousemove。

D. 懶惰提交
任意兩次提交的間隔時間,必須大於一個指定時間,纔會促成有效提交;即不給休息不幹活。app

module.submit = debounce(150, function() { // todo }) 

仍是以知乎草稿舉例,當在編輯器內按下 ctrl + s 時,能夠手動保存草稿;若是你連按,程序會表示不理解爲何你要連按,只有等你放棄連按,它纔會繼續。

============
更多記憶中的例子

方式 C 和 方式 D 有時更加通用,好比這些狀況:編輯器

  • 遊戲中你撿到一把威力強大的高速武器,爲了防止你的子彈在屏幕上打成一條直線,能夠 throttle 來控制頻率;
  • 在彈幕型遊戲裏,爲了防止你把射擊鍵夾住來進行無腦遊戲,能夠用 debounce 來控制頻率;
  • 在編譯任務裏,守護進程監視了某一文件夾裏全部的文件(如任一文件的改變均可以觸發從新編譯,一次執行就須要2秒),但某種操做可以瞬間形成大量文件改變(如 git checkout),這時一個簡單的 debounce 可使編譯任務只執行一次。


而方式 C 甚至能夠和方式 B 組合使用,好比自動完成組件(Google 首頁的搜索就是):函數

  • 當用戶快速輸入文本時(特別是打字能手),能夠 throttle keypress 事件處理函數,以指定時間間隔來提取文本域的值,而後當即進行新的查詢;
  • 當新的查詢須要發送,但上一個查詢還沒返回結果時,能夠 abort 未完成的查詢,並當即發送新查詢;


----- update 2013-01-08 -----
E. 記憶型post

var scrape = memoize(function(url) { return $.post('/scraper', { 'url': url }) }) 

對於一樣的參數,其返回始終結果是恆等的——每次都將返回同一對象。
應用例子有編輯器,如粘貼內容時抓取其中的連接信息,memoize 用以保證一樣的連接不會抓取兩次。

----- update 2013-03-27 -----
F. 累積型
前幾天處理自動完成事件時獲得這個函數,發現也能夠用在處理連續事件上,它可以把連續的屢次提交合併爲一個提交,好比:this

var request = makePile(5, function() { $.post('/', { list: JSON.stringify([].slice.call(arguments)) }) }) // 連續發送五次 request({a:1}), request({a:2}), request({a:3}), request({a:4}), request({a:5}) /* post => list:[{"a":1},{"a":2},{"a":3},{"a":4},{"a":5}]  */ 

樣例實現:url

var makePile = function(count, onfilter, onvalue) { var values = [], id = function(value) { return value } return function(value) { values.push((onvalue || id).apply(this, arguments)) if (values.length === count) { onfilter.apply(this, values) values = [] } } } 


----- update 2013-04-16 -----
另外一種累積是按時間而不是次數,好比應用在行爲統計上,可能在瞬間收集到數十上百相似的行爲,這時能夠用上面 pile 的結構加上 debounce 來防止大批重複請求(但又不丟失任何統計):

var trackFactory = function(delay, action) { var params = [], slice = [].slice var touch = debounce(delay, function() { if (params.length) { action(params) params = [] } }) return function() { params.push(slice.call(arguments)) touch() } } var track = trackFactory(550, function(params) { // send tracking request }) 


G. 採樣型
這是最近重構時聯想到的,一種和上面都不一樣的去重操做,能夠應用在自動加載(timeline)行爲控制上:

autoload.listen(feeds, 'next', sample(3, function() {
  this.enable()
}))


若是 sample 是固化的選擇函數(n 選 1),它這實際上會這樣工做:

O-O-X-O-O-X


但「自動加載」的應用可能想要的是(兩次自動,一次手動):

X-X-O-X-X-O


對於這種狀況,能夠定義做爲配置的選擇函數來實現控制:

options { sample: (n) => n % 3 !== 0 }

即每一個下一次加載完成以後, 每三次有兩次對下一次加載實行自動加載。

相關文章
相關標籤/搜索