Debounce 防彈跳 & Throttle 節流
Play this article
簡介
Debounce 和 Throttle 常用在 input
、scroll
、resize
等事件進行控制,防止事件過多觸發,減少資源消耗或是商業邏輯上的維護成本。這兩者最大的差異在於觸發的時機。
debounce(func, wait, options)
:建立並回傳函數的防彈跳版本,將延遲函數的執行(真正的執行)在函數最後一次調用時刻的 wait 毫秒之後,對於必須在一些輸入(多是一些用戶操作)停止之後再執行的行為有幫助。將一個連續的調用歸類為一個,如果連續在 wait 毫秒內調用,只有最後一次會執行throttle(func, wait, options)
:建立並返回一個像節流閥一樣的函數,當重復調用函數的時候,最多每隔指定的 wait 毫秒調用一次該函數;不允許方法在每 wait 毫秒間執行超過一次,如果連續在 wait 毫秒內調用,最後執行會均勻分布在大約每 wait 一次
使用場景
- 滑鼠移動時減少計算次數:debounce
- 在輸入框中輸入文字時自動發送 Ajax 請求進行自動補全: debounce
- API 請求合併,不希望短時間內大量的請求被重復發送:debounce
- 縮放畫面重新計算樣式或佈局:debounce 或 throttle
- 滾動時觸發操作:throttle
- 對使用者輸入的驗證,不想停止輸入再進行驗證,而是每 n 秒進行驗證:throttle
Debounce 防彈跳
在事件被觸發 n 秒後,再去執行 callback。如果 n 秒內被重新觸發,則重新計時。
基本函數架構
function debounce(delay, callback) {
let timeoutID;
function wrapper() {
const self = this;
const args = arguments;
function exec() {
callback.apply(self, args);
}
// 重新計時
clearTimeout(timeoutID);
timeoutID = setTimeout(exec, delay);
}
return wrapper;
}
使用範例
let reduceEvent
function debounce(cb, delay) {
if (!reduceEvent) {
reduceEvent = setTimeout(() => {
cb();
console.log('執行啦!!');
reduceEvent = null;
}, delay);
}
}
setTimeout(() => debounce(() => console.log(1), 2000), 1000) // Output: 1 執行啦!!
setTimeout(() => debounce(() => console.log(2), 2000), 2000)
setTimeout(() => debounce(() => console.log(3), 2000), 2000)
setTimeout(() => debounce(() => console.log(4), 2000), 4000) // Output: 4 執行啦!!
Throttle 節流
在規定時間內,將觸發的事件合併成一次執行。降低高頻率事件觸發的頻次
function throttle(method, wait) {
// 對比時間,初始化為 0 則首次觸發立即執行,初始化為當前時間,則 wait 毫秒後觸發才會執行
let previous = 0;
return function(...args) {
let context = this;
let now = new Date().getTime();
// 間隔大於 wait 則執行 method 並更新對比時間戳
if (now - previous > wait) {
method.apply(context, args);
previous = now;
}
}
}
function throttle(method, wait) {
let timeout;
return function(...args) {
let context = this;
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
method.apply(context, args);
}, wait);
}
}
}
使用範例
function throttle(func, wait = 200) {
let last = 1
let timer
return function(...rest) {
const now = +new Date()
if (last && now - last < wait) {
clearTimeout(timer)
timer = setTimeout(() => {
last = now
func.apply(this, rest)
}, wait)
} else {
last = now
func.apply(this, rest)
clearTimeout(timer)
}
}
}
const task = throttle(() => console.log(1), 2000)
setTimeout(task, 0)
setTimeout(task, 500)
setTimeout(task, 1000)
setTimeout(task, 2000) // Output: 1 1