節流函式的實現

2022-06-14 22:12:12 字數 3516 閱讀 6398

函式節流指的是某個函式在一定時間間隔內(例如 3 秒)只執行一次,在這 3 秒內無視後來產生的函式呼叫請求,也不會延長時間間隔。3 秒間隔結束後第一次遇到新的函式呼叫會觸發執行,然後在這新的 3 秒內依舊無視後來產生的函式呼叫請求,以此類推。

舉乙個小例子,不知道大家小時候有沒有養過小金魚啥的,養金魚肯定少不了接水,剛開始接水時管道中水流很大,水到半滿時開始擰緊水龍頭,減少水流的速度變成 3 秒一滴,通過滴水給小金魚增加氧氣。

此時「管道中的水」就是我們頻繁操作事件而不斷湧入的**任務,它需要接受「水龍頭」安排;「水龍頭」就是節流閥,控制水的流速,過濾無效的**任務;「滴水」就是每隔一段時間執行一次函式,「3 秒」就是間隔時間,它是「水龍頭」決定「滴水」的依據。

如果你還無法理解,看下面這張圖就清晰多了,另外點選 這個頁面 檢視節流和防抖的視覺化比較。其中 regular 是不做任何處理的情況,throttle 是函式節流之後的結果,debounce 是函式防抖之後的結果(下一小節介紹)。

函式節流非常適用於函式被頻繁呼叫的場景,例如:window.onresize() 事件、mousemove 事件、上傳進度等情況。使用 throttle api 很簡單,那應該如何實現 throttle 這個函式呢?

實現方案有以下兩種

這裡我們採用第一種方案來實現,通過閉包儲存乙個 previous 變數,每次觸發 throttle 函式時判斷當前時間和 previous 的時間差,如果這段時間差小於等待時間,那就忽略本次事件觸發。如果大於等待時間就把 previous 設定為當前時間並執行函式 fn。

我們來一步步實現,首先實現用閉包儲存 previous 變數。

const throttle = (fn, wait) => 

}複製**

執行 throttle 函式後會返回乙個新的 function,我們命名為 betterfn。

const betterfn = function(...args) 

複製**

betterfn 函式中可以獲取到 previous 變數值也可以修改,在**監聽或事件觸發時就會執行 betterfn,即betterfn(),所以在這個新函式內判斷當前時間和 previous 的時間差即可。

const betterfn = function(...args) 

}複製**

結合上面兩段**就實現了節流函式,所以完整的實現如下。

// fn 是需要執行的函式

// wait 是時間間隔

const throttle = (fn, wait = 50) =>

}}// demo

// 執行 throttle 函式返回新函式

const betterfn = throttle(() => console.log('fn 函式執行了'), 1000)

// 每 10 毫秒執行一次 betterfn 函式,但是只有時間差大於 1000 時才會執行 fn

setinterval(betterfn, 10)

複製**

上述**實現了乙個簡單的節流函式,不過 underscore 實現了更高階的功能,即新增了兩個功能

配置 時,事件剛開始的那次**不執行;配置 時,事件結束後的那次**不執行,不過需要注意的是,這兩者不能同時配置。

所以在 underscore 中的節流函式有 3 種呼叫方式,預設的(有頭有尾),設定 的,以及設定 的。上面說過實現 throttle 的方案有 2 種,一種是通過時間戳判斷,另一種是通過定時器建立和銷毀來控制。

第一種方案實現這 3 種呼叫方式存在乙個問題,即事件停止觸發時無法響應**,所以 時無法生效。

第二種方案來實現也存在乙個問題,因為定時器是延遲執行的,所以事件停止觸發時必然會響應**,所以 時無法生效。

underscore 採用的方案是兩種方案搭配使用來實現這個功能。

const throttle = function(func, wait, options) ;

var later = function() 時

// 每次觸發**函式後設定 previous 為 0

// 不然為當前時間

previous = options.leading === false ? 0 : _.now();

// 防止記憶體洩漏,置為 null 便於後面根據 !timeout 設定新的 timeout

timeout = null;

// 執行函式

if (!timeout) context = args = null;

};// 每次觸發事件**都執行這個函式

// 函式內判斷是否執行 func

// func 才是我們業務層**想要執行的函式

var throttled = function() (表示第一次**不執行)

// 此時設定 previous 為當前值,表示剛執行過,本次就不執行了

if (!previous && options.leading === false) previous = now;

// 距離下次觸發 func 還需要等待的時間

var remaining = wait - (now - previous);

context = this;

args = arguments;

// 要麼是到了間隔時間了,隨即觸發方法(remaining <= 0)

// 要麼是沒有傳入 ,且第一次觸發**,即立即觸發

// 此時 previous 為 0,wait - (now - previous) 也滿足 <= 0

// 之後便會把 previous 值迅速置為 now

if (remaining <= 0 || remaining > wait)

// 設定 previous 為當前時間

previous = now;

// 執行 func 函式

if (!timeout) context = args = null;

} else if (!timeout && options.trailing !== false) ,即最後一次不需要觸發了,也不會進入這個分支

// 間隔 remaining milliseconds 後觸發 later 方法

timeout = settimeout(later, remaining);

}return result;

};// 手動取消

throttled.cancel = function() ;

// 執行 _.throttle 返回 throttled 函式

return throttled;

};複製**

節流實現方案有 2 種

underscore.js

前端效能優化原理與實踐

underscore 函式節流的實現

閉包實現函式節流

在一些業務場景中我們可能會用到window.onresize事件或者mousemove等監控型別事件,當使用者做出相關操作的時候,會平凡觸發相應的函式,有可能每秒會觸發幾十甚至上百次,如果函式中有一些dom操作,就會導致瀏覽器卡頓等現象,很多時候我們根本不需要執行這麼多次,這時我們就需要用到函式節流...

簡單實現函式防抖 函式節流

節流概念 限制函式觸發頻率,規定時間內只能觸發一次。實現 function throttle fun,t t fun arguments document.getelementbyid element onclick throttle 1000 防抖概念 連續觸發事件,只觸發一次,只有中斷指定的時間...

js實現防抖函式和節流函式

含義 防抖函式指的是在特定的時間內沒有再次觸發,才得以進行接下來的函式執行 用途 當window.onresize不斷的調整大小的時候,為了避免不斷的重排與重繪,可以用防抖函式設定在onresize完成後一段時間內不再有視窗大小變動,此時再進行dom的重排重繪 function debounce f...