摘要:多次連續事件觸發動作最后一次觸發之后的指定時間間隔執行回調函數預先設定一個執行周期,當調用動作的時刻大于等于執行周期則執行該動作,然后進入下一個新的時間周期。
定義
為了避免某個事件在較短的時間段內(稱為 T)內連續觸發從而引起的其對應的事件處理函數不必要的連續執行的一種事件處理機制(高頻觸發事件解決方案)
debounce:當調用動作觸發一段時間后,才會執行該動作,若在這段時間間隔內又調用此動作則將重新計算時間間隔。(多次連續事件觸發動作/最后一次觸發之后的指定時間間隔執行回調函數)
throttle:預先設定一個執行周期,當調用動作的時刻大于等于執行周期則執行該動作,然后進入下一個新的時間周期。(每個指定時間執行一次回調函數,可以指定時間間隔之前調用)
1、throttle 保證了在每個 T 內至少執行一次,而 debounce 沒有這樣的保證
2、每次事件觸發時參考的時間點,對于debounce來是上一次事件觸發的時間并且在延時沒有結束時會重置延時;
throttle 是上一次 handler 執行的時間并且在延時尚未結束時不會重置延時
響應速度跟不上觸發頻率,往往會出現延遲,導致假死或者卡頓感
實現 去抖 debounce空閑控制:所有操作最后一次性執行
【簡潔版】
/** * @param fn {Function} 實際要執行的函數 * @param delay {Number} 延遲時間,也就是閾值,單位是毫秒(ms) * @return {Function} 返回一個“去彈跳”了的函數 */ function debounce(fn, delay) { // 定時器,用來 setTimeout var timer // 返回一個函數,這個函數會在一個時間區間結束后的 delay 毫秒時執行 fn 函數 return function () { // 保存函數調用時的上下文和參數,傳遞給 fn var context = this var args = arguments // 每次這個返回的函數被調用,就清除定時器,以保證不執行 fn clearTimeout(timer) // 當返回的函數被最后一次調用后(也就是用戶停止了某個連續的操作), // 再過 delay 毫秒就執行 fn timer = setTimeout(function () { fn.apply(context, args) }, delay) } }
【完整版】
// immediate: 是否立即執行回調函數; 其它參數同上 function debounce(fn, wait, immediate) { let timer = null return function() { let args = [].slice.call(arguments) if (immediate && !timer) { fn.apply(this, args) } if (timer) clearTimeout(timer) timer = setTimeout(() => { //箭頭函數,this指向外層環境 fn.apply(this, args) }, wait) } }
// 測試: var fn = function() { console.log("debounce..") } oDiv.addEventListener("click", debounce(fn, 3000))節流 throttle
固定頻次:減少執行頻次,每隔一定時間執行一次
【簡潔版】
/** * 固定回調函數執行的頻次 * @param fn {Function} 實際要執行的函數 * @param interval {Number} 執行間隔,單位是毫秒(ms) * * @return {Function} 返回一個“節流”函數 */ var throttle = function (fn, interval) { // 記錄前一次時間 var last = +new Date() var timer = null // 包裝完后返回 閉包函數 return function () { var current = +new Date() var args = [].slice.call(arguments, 0) var context = this // 首先清除定時器 clearTimeout(timer) // current 與last 間隔大于interval 執行一次fn // 在一個周期內 last相對固定 current一直再增加 // 這里可以保證調用很密集的情況下 current和last 必須是相隔interval 才會調用fn if (current - last >= interval) { fn.apply(context, args) last = current } else { // 如果沒有大于間隔 添加定時器 // 這可以保證 即使后面沒有再次觸發 fn也會在規定的interval后被調用 timer = setTimeout(function() { fn.apply(context, args) last = current }, interval-(current - last)) } } }
【完整版】
/** * 頻率控制 返回函數連續調用時,func 執行頻率限定為 次 / wait * 自動合并 data * * 若無 option 選項,或者同時為true,即 option.trailing !== false && option.leading !== false,在固定時間開始時刻調用一次回調,并每個固定時間最后時刻調用回調 * 若 option.trailing !== false && option.leading === false, 每個固定時間最后時刻調用回調 * 若 option.trailing === false && option.leading !== false, 只會在固定時間開始時刻調用一次回調 * 若同時為false 則不會被調用 * * @param {function} func 傳入函數 * @param {number} wait 表示時間窗口的間隔 * @param {object} options 如果想忽略開始邊界上的調用,傳入{leading: false}。默認undefined * 如果想忽略結尾邊界上的調用,傳入{trailing: false}, 默認undefined * @return {function} 返回客戶調用函數 */ function throttle (func, wait, options) { var context, args, result; var timeout = null; // 上次執行時間點 var previous = 0; if (!options) { options = {}; } // 延遲執行函數 function later () { // 若設定了開始邊界不執行選項,上次執行時間始終為0 previous = options.leading === false ? 0 : Date.now(); timeout = null; result = func.apply(context, args); if (!timeout) { context = args = null; } } return function (handle, data) { var now = Date.now(); // 首次執行時,如果設定了開始邊界不執行選項,將上次執行時間設定為當前時間。 if (!previous && options.leading === false) { previous = now; } // 延遲執行時間間隔 var remaining = wait - (now - previous); context = this; args = args ? [handle, Object.assign(args[1], data)] : [handle, data]; // 延遲時間間隔remaining小于等于0,表示上次執行至此所間隔時間已經超過一個時間窗口 // remaining大于時間窗口wait,表示客戶端系統時間被調整過 if (remaining <= 0 || remaining > wait) { 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 } }運用
游戲射擊,keydown 事件
文本輸入、自動完成,keyup 事件
鼠標移動,mousemove 事件
DOM 元素動態定位,window 對象的 resize 和 scroll 事件
前兩者 debounce 和 throttle 都可以按需使用;后兩者肯定是用 throttle
underscore 實現源碼 debounce_.debounce = function(func, wait, immediate) { var timeout, result; var later = function(context, args) { timeout = null; if (args) result = func.apply(context, args); }; var debounced = restArgs(function(args) { if (timeout) clearTimeout(timeout); if (immediate) { var callNow = !timeout; timeout = setTimeout(later, wait); if (callNow) result = func.apply(this, args); } else { timeout = _.delay(later, wait, this, args); } return result; }); debounced.cancel = function() { clearTimeout(timeout); timeout = null; }; return debounced; };throttle
_.throttle = function(func, wait, options) { var timeout, context, args, result; 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; }; var throttled = 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; }; throttled.cancel = function() { clearTimeout(timeout); previous = 0; timeout = context = args = null; }; return throttled; };
【參考】
https://blog.coding.net/blog/...
https://github.com/lishengzxc...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/105533.html
摘要:上段代碼的一個問題是,事件會在定時器結束后被觸發,因此會出現一定的延遲,如果想讓事件被立即觸發,可以使用以下的去抖函數但是,對于去抖來說,在某些場景下是不合適的,因此我們可以使用節流。 參考文章游戲星人眼中的節流與去抖(很生動) 函數去抖與節流 Debounce:函數去抖就是對于一定時間段的連續的函數調用,只讓其執行一次Throttle:函數節流就是讓連續執行的函數,變成固定時間段間斷...
摘要:可以看下面的栗子這個圖中圖中每個小格大約,右邊有原生事件與節流去抖插件的與事件。即如果有連續不斷的觸發,每執行一次,用在每隔一定間隔執行回調的場景。執行啦打印執行啦打印執行啦節流按照上面的說明,節流就是連續多次內的操作按照指定的間隔來執行。 一般在項目中我們會對input、scroll、resize等事件進行節流控制,防止事件過多觸發,減少資源消耗;在vue的官網的例子中就有關于lod...
摘要:去抖主要針對的是頻繁觸發某個事件后,然后進行后續處理的場景。常見的就是頻繁輸入停止假設后進行查詢等操作。函數接口定義實際需要調用的函數空閑時間返回調用函數函數接口定義延遲時間需要調用的函數返回函數 前言 做過前端的童鞋應該都知道lodash這個強大的使用工具庫。為什么要寫這篇文章呢,主要今天遇到一個問題,socket推送消息太頻繁,導致saga頻繁更新,頁面有所卡頓,需要通過函數節流控...
摘要:函數節流和去抖的出現場景,一般都伴隨著客戶端的事件監聽。函數節流的核心是,讓一個函數不要執行得太頻繁,減少一些過快的調用來節流。 概述 也是好久沒更新 源碼解讀,看著房價蹭蹭暴漲,心里也是五味雜陳,對未來充滿恐懼和迷茫 ...(敢問一句你們上岸了嗎) 言歸正傳,今天要介紹的是 underscore 中兩個重要的方法,函數節流和函數去抖。這篇文章不會涉及具體的代碼實現(關于代碼實現請期...
摘要:節流保證在一定時間內,只能觸發一次。我們在嘗試一下去抖消抖,消除抖動,感覺這個更好聽有沒有什么現成的上的一次發現源碼的經歷以及對學術界拿來主義的思考函數節流和函數去抖應用場景辨析函數去抖的實現 開篇先提幾個問題? 1.做搜索框的時候你使用什么事件?change?blur?keyup?你想要的效果是什么? 2.scroll事件怎么就觸發?是滾一段距離觸發一次?還是滾一圈觸發一次?還是滾...
閱讀 2482·2021-11-19 09:59
閱讀 2008·2019-08-30 15:55
閱讀 939·2019-08-29 13:30
閱讀 1346·2019-08-26 10:18
閱讀 3093·2019-08-23 18:36
閱讀 2394·2019-08-23 18:25
閱讀 1168·2019-08-23 18:07
閱讀 443·2019-08-23 17:15