節流和防抖在開發項目過程當中很常見,例如 input 輸入實時搜索、scrollview 滾動更新了,等等,大量的場景須要咱們對其進行處理。咱們由 Lodash 來介紹,直接進入主題吧。前端
防抖 (debounce) :屢次觸發,只在最後一次觸發時,執行目標函數。git
lodash.debounce(func, [wait=0], [options={}])
節流(throttle):限制目標函數調用的頻率,好比:1s內不能調用2次。github
lodash.throttle(func, [wait=0], [options={}])
lodash 在 opitons 參數中定義了一些選項,主要是如下三個:緩存
根據 leading 和 trailing 的組合,能夠實現不一樣的調用效果:app
deboucne 還有 cancel 方法,用於取消防抖動調用函數
防抖 (debounce):this
addEntity = () => { console.log('--------------addEntity---------------') this.debounceFun(); } debounceFun = lodash.debounce(function(e){ console.log('--------------debounceFun---------------'); }, 500,{ leading: true, trailing: false, })
首次點擊時執行,連續點擊且時間間隔在500ms以內,再也不執行,間隔在500ms以外再次點擊,執行。code
節流(throttle):對象
addEntity = () => { console.log('--------------addEntity---------------'); this.throttleFun(); } throttleFun = lodash.throttle(function(e){ console.log('--------------throttleFun---------------'); }, 500,{ leading: true, trailing: false, })
首次點擊時執行,連續點擊且間隔在500ms以內,500ms以後自動執行一次(注:連續點擊次數時間以後小於500ms,則不會自動執行),間隔在500ms以外再次點擊,執行。blog
// 這個是用來獲取當前時間戳的 function now() { return +new Date() } /** * 防抖函數,返回函數連續調用時,空閒時間必須大於或等於 wait,func 纔會執行 * * @param {function} func 回調函數 * @param {number} wait 表示時間窗口的間隔 * @param {boolean} immediate 設置爲ture時,是否當即調用函數 * @return {function} 返回客戶調用函數 */ function debounce (func, wait = 50, immediate = true) { let timer, context, args // 延遲執行函數 const later = () => setTimeout(() => { // 延遲函數執行完畢,清空緩存的定時器序號 timer = null // 延遲執行的狀況下,函數會在延遲函數中執行 // 使用到以前緩存的參數和上下文 if (!immediate) { func.apply(context, args) context = args = null } }, wait) // 這裏返回的函數是每次實際調用的函數 return function(...params) { // 若是沒有建立延遲執行函數(later),就建立一個 if (!timer) { timer = later() // 若是是當即執行,調用函數 // 不然緩存參數和調用上下文 if (immediate) { func.apply(this, params) } else { context = this args = params } // 若是已有延遲執行函數(later),調用的時候清除原來的並從新設定一個 // 這樣作延遲函數會從新計時 } else { clearTimeout(timer) timer = later() } } }
/** * underscore 節流函數,返回函數連續調用時,func 執行頻率限定爲 次 / wait * * @param {function} func 回調函數 * @param {number} wait 表示時間窗口的間隔 * @param {object} options 若是想忽略開始函數的的調用,傳入{leading: false}。 * 若是想忽略結尾函數的調用,傳入{trailing: false} * 二者不能共存,不然函數不能執行 * @return {function} 返回客戶調用函數 */ _.throttle = function(func, wait, options) { var context, args, result; var timeout = null; // 以前的時間戳 var previous = 0; // 若是 options 沒傳則設爲空對象 if (!options) options = {}; // 定時器回調函數 var later = function() { // 若是設置了 leading,就將 previous 設爲 0 // 用於下面函數的第一個 if 判斷 previous = options.leading === false ? 0 : _.now(); // 置空一是爲了防止內存泄漏,二是爲了下面的定時器判斷 timeout = null; result = func.apply(context, args); if (!timeout) context = args = null; }; return function() { // 得到當前時間戳 var now = _.now(); // 首次進入前者確定爲 true // 若是須要第一次不執行函數 // 就將上次時間戳設爲當前的 // 這樣在接下來計算 remaining 的值時會大於0 if (!previous && options.leading === false) previous = now; // 計算剩餘時間 var remaining = wait - (now - previous); context = this; args = arguments; // 若是當前調用已經大於上次調用時間 + wait // 或者用戶手動調了時間 // 若是設置了 trailing,只會進入這個條件 // 若是沒有設置 leading,那麼第一次會進入這個條件 // 還有一點,你可能會以爲開啓了定時器那麼應該不會進入這個 if 條件了 // 其實仍是會進入的,由於定時器的延時 // 並非準確的時間,極可能你設置了2秒 // 可是他須要2.2秒才觸發,這時候就會進入這個條件 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) { // 判斷是否設置了定時器和 trailing // 沒有的話就開啓一個定時器 // 而且不能不能同時設置 leading 和 trailing timeout = setTimeout(later, remaining); } return result; }; };
走在最後,歡迎 star:https://github.com/sisterAn/blog
歡迎關注:前端瓶子君