再處理網頁互動時,常常會有需要處理事件,有些事件會被呼叫的太頻繁而導致效能低落。例如 onScroll callback,就會被常常呼叫到在做重新 render 的動作。
簡單的解法可以設定計算量大的事件函數每 250 ms 執行一次。而目前已有眾多可行的解法處理這個問題,較常見的解法有 throttling 和 debouncing 等等。
Debounce 和 throttle 的視覺化範例 http://demo.nimius.net/debounce_throttle/
讓一個函式在連續觸發時只執行一次
常見的用法是使用者連續輸入基本資訊後,才觸發事件處理器進行格式確認。
const debounce = (func, delay) => {
let timer = null;
return function () {
let context = this;
let args = arguments;
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(context, args)
}, delay);
}
}
const handleScroll = () => {
console.log(window.scrollY)
}
window.addEventListener(‘scroll’, debounce(handleScroll));
程式碼很直觀,先設一個計時器 (timer),保存當下脈絡後 (context, args),只要太早進來 (小於 delay) 就會重置計時器,直到成功執行 setTimeout 內的函式後結束。
讓一個函數不要執行得太頻繁
減少一些過快的呼叫來節流。一個常見的用法是減少 scroll 的觸發頻率,因為 scroll 常常綁定一些消耗資源的 render 的事件。
const throttle = (func, threshhold) => {
let last;
let timer;
if (threshhold) {
threshhold = 250;
}
return () => {
let context = this
let args = arguments
let now = +new Date()
if (last && now < last + threshhold) {
clearTimeout(timer)
timer = setTimeout(() => {
last = now
func.apply(context, args)
}, threshhold)
} else {
last = now
fn.apply(context, args)
}
}
}
const handleScroll = () => {
console.log(window.scrollY)
}
window.addEventListener(‘scroll’, debounce(handleScroll));
與 debouncing 的程式邏輯相似,只多了一個時間間隔的判斷。
說了那麼多,目前 lodash 都有提供 debounce 與 throttle 的 function 可以使用,那真是太好了。
Reference