什么是节流 (throttle)? 如何实践节流 (throttle) 函式?
2022年12月11日
💎 加入 E+ 成長計畫 如果你喜歡我們的內容,歡迎加入 E+,獲得更多深入的軟體前後端內容
防抖(debounce) 和节流(throttle) 绝对是在考手写题时最常出现的前几名,这两者都能做到优化,但使用情境不太相同,本篇介绍节流(throttle)函式,在这篇文章中会继续讨论防抖。
节流 (throttle) 在做什么?
节流 (throttle) 指的是,在一段时间内只会执行一次触发事件的回调 (callback) 函式,若在这之中又有新事件触发,则不执行此回调函式。
在手写节流 (throttle) 函式前,我们先来了解节流的使用情境。
监听滚动事件,是搭配节流的使用场景之一。举例来说,要判断使用者是否已经滑动到页面的 30% 处,当到达时会触发一些动画效果,因此,会透过监听滚动事件时计算是否已到达该位置,但如果只要一滚动就计算会非常消耗性能,透过节流(throttle) 可以将计算的这个回调函式在固定时间内合并一次被执行。
这里不适合使用防抖的原因是,防抖只会在事件停止被触发后的一段时间内被执行一次。因此如果用防抖,当使用者一直滑动页面,函式就永远不会被触发。这边我们仍想要函式在滑动过程中被触发,只是不想那么频繁被触发,这种情境下,节流就可以派上用场。
节流函式会接受两个参数
- 延迟的时间 (ms)
- 要执行的回调 (callback) 函式
手写节流 (throttle) 函式
我们先直接看代码,看看你能了解多少。有不懂的地方也不担心,下面会透过注解,一行行解释:
function throttle(fn, delay = 500) {
let timer = null;
return (...args) => {
if (timer) return;
timer = setTimeout(() => {
fn(...args);
timer = null;
}, delay);
};
}
代码注解版本和实际应用
function throttle(fn, delay = 500) {
let timer = null;
// throttle 本身会回传一个函式,透过 ...args 拿到该函式的引数
return function (...args) {
// 如果有计时器,表示还在 delay 的秒数内
// 直接 return,不往下执行代码
if (timer) return;
// 如果计时器不等于 null,会进到以下逻辑
// 设定计时器,在 delay 秒数之后,将计时器值为改为 null
// 如果还不到 delay 的秒数,再执行一次的话
// 因为 timer 的值不为 null,前面就先 return 不会进到这段逻辑
// 可以达到 throttle 的目的,将一段时间内的操作,集合成一次执行
timer = setTimeout(() => {
timer = null;
}, delay);
// 直到时间到了后,timer 变成 null,才能够执行函式
// 用 .apply 来呼叫,才能
fn.apply(this, args);
};
}
const updateThrottleText = throttle(() => {
console.log("throttle");
}, 500);
// 如果一直滑动画面,会固定 500ms console.log('throttle')
window.addEventListener("scroll", () => {
updateThrottleText();
});