函數防抖與節流 underscore.js提供了很多很有用的函數,今天想說說其中的兩個。這兩個函數都用於限制函數的執行 debounce 在解釋這個函數前,我們先從一個例子看下這個函數的使用場景。假設我們網站有個搜索框,用戶輸入文本我們會自動聯想匹配出一些結果供用戶選擇。我們可能首先想到的做法就是監 ...
函數防抖與節流 underscore.js提供了很多很有用的函數,今天想說說其中的兩個。這兩個函數都用於限制函數的執行 debounce 在解釋這個函數前,我們先從一個例子看下這個函數的使用場景。假設我們網站有個搜索框,用戶輸入文本我們會自動聯想匹配出一些結果供用戶選擇。我們可能首先想到的做法就是監聽keypress事件,然後非同步去查詢結果。這個方法本身是沒錯的,但是如果用戶快速的輸入了一連串的字元,假設是10個字元,那麼就會在瞬間觸發了10次的請求,這無疑不是我們想要的。我們想要的是用戶停止輸入的時候才去觸發查詢的請求,這時候函數防抖可以幫到我們。 函數防抖就是讓某個函數在上一次執行後,滿足等待某個時間內不再觸發此函數後再執行,而在這個等待時間內再次觸發此函數,等待時間會重新計算。 我們先看下underscore.js里相關函數的定義:
_.debounce(function, wait, [immediate])// Returns a function, that, as long as it continues to be invoked, will not// be triggered. The function will be called after it stops being called for// N milliseconds. If `immediate` is passed, trigger the function on the// leading edge, instead of the trailing. _.debounce = function(func, wait, immediate) { var timeout, args, context, timestamp, result; var later = function() { var last = _.now() - timestamp; if (last < wait && last >= 0) { timeout = setTimeout(later, wait - last); } else { timeout = null; if (!immediate) { result = func.apply(context, args); if (!timeout) context = args = null; } } }; return function() { context = this; args = arguments; timestamp = _.now(); var callNow = immediate && !timeout; if (!timeout) timeout = setTimeout(later, wait); if (callNow) { result = func.apply(context, args); context = args = null; } return result; }; }; 參數function是需要進行函數防抖的函數;參數wait則是需要等待的時間,單位為毫秒;immediate參數如果為true,則debounce函數會在調用時立刻執行一次function,而不需要等到wait這個時間後,例如防止點擊提交按鈕時的多次點擊就可以使用這個參數。 所以,上面那個場景,我們可以這麼解決: function query() { //進行非同步調用查詢 } var lazyQuery = _.debounce(query, 300); $('#search').keypress(lazyQuery);
throttle
我們網站經常會有這樣的需求,就是滾動瀏覽器滾動條的時候,更新頁面上的某些佈局內容或者去調用後臺的某介面查詢內容。同樣的,如果不對函數調用的頻率加以限制的話,那麼可能我們滾動一次滾動條就會產生N次的調用了。但是這次的情況跟上面的有所不同,我們不是要在每完成等待某個時間後去執行某函數,而是要每間隔某個時間去執行某函數,避免函數的過多執行,這個方式就叫函數節流。 同樣的,我們看下underscore.js里相關函數的定義:_.throttle(function, wait, [options])// Returns a function, that, when invoked, will only be triggered at most once// during a given window of time. Normally, the throttled function will run// as much as it can, without ever going more than once per `wait` duration;// but if you'd like to disable the execution on the leading edge, pass// `{leading: false}`. To disable execution on the trailing edge, ditto. _.throttle = function(func, wait, options) { var context, args, result; var timeout = null; var previous = 0; if (!options) options = {}; var later = function() { previous = options.leading === false ? 0 : _.now(); timeout = null; result = func.apply(context, args); if (!timeout) context = args = null; }; return function() { var now = _.now(); if (!previous && options.leading === false) previous = now; var remaining = wait - (now - previous); context = this; args = arguments; if (remaining <= 0 || remaining > wait) { if (timeout) { clearTimeout(timeout); timeout = null; } previous = now; result = func.apply(context, args); if (!timeout) context = args = null; } else if (!timeout && options.trailing !== false) { timeout = setTimeout(later, remaining); } return result; }; }; 參數function是需要進行函數節流的函數;參數wait則是函數執行的時間間隔,單位是毫秒。option有兩個選項,throttle第一次調用時預設會立刻執行一次function,如果傳入{leading: false},則第一次調用時不執行function。{trailing: false}參數則表示禁止最後那一次延遲的調用。具體可以看源碼進行理解。 所以,在滾動滾動條的場景,我們可以這麼做: function handleScroll() { //進行滾動時的相關處理 } var throttled = _.throttle(handleScroll, 100); $(window).scroll(throttled);