节流
函数节流
高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率。
当持续触发事件时,保证一定时间段内只调用一次事件处理函数。
规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。
节流实现原理: 维护一个计时器,规定在 delay 时间后触发函数,但是在 delay 时间内再次触发的话,会判断是否有延迟调用函数未执行,有则返回,没有则设定在 delay 时间后触发函数
实现方式:每次触发事件时,如果当前有等待执行的延时函数,则直接return
//节流throttle代码:
function throttle(fn,delay) {
let canRun = true; // 通过闭包保存一个标记
return function () {
//在函数开头判断标记是否为true,不为true则return
if (!canRun) return;
//立即设置为false
canRun = false;
//将外部传入的函数的执行放在setTimeout中
setTimeout(() => {
//最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。
//当定时器没有执行的时候标记永远是false,在开头被return掉
fn.apply(this, arguments);
canRun = true;
}, delay);
};
}
function sayHi(e) {
console.log('节流:', e.target.innerWidth, e.target.innerHeight);
}
window.addEventListener('resize', throttle(sayHi,500));
应用场景
鼠标连续不断地触发某事件(如点击),只在单位时间内只触发一次;
在页面的无限加载场景下,需要用户在滚动页面时,每隔一段时间发一次 ajax 请求,而不是在用户停下滚动页面操作时才去请求数据;
监听滚动事件,比如是否滑到底部自动加载更多,用 throttle 来判断;
function throttle(fn, ms) {
let timer;
return function (...args) {
if (timer) return;
canRun = false;
timer = setTimeout(() => {
fn(...args);
timer = null;
}, ms);
};
}
hooks 节流
function useThrottle(fn, delay, dep = []) {
const { current } = useRef({ fn, timer: null });
useEffect(
function () {
current.fn = fn;
},
[fn]
);
return useCallback(function f(...args) {
if (!current.timer) {
current.timer = setTimeout(() => {
delete current.timer;
}, delay);
current.fn.call(this, ...args);
}
}, dep);
}