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