一個簡易的需求,點一個按鈕,則向服務器請求資源,不做處理時,屢次點擊後會有不少個請求在等待。粗暴的解決方式是點一次就將按鈕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 有時更加通用,好比這些狀況:編輯器
而方式 C 甚至能夠和方式 B 組合使用,好比自動完成組件(Google 首頁的搜索就是):函數
----- 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 }
即每一個下一次加載完成以後, 每三次有兩次對下一次加載實行自動加載。