简单throttle
写一个throttle很简单,每次运行函数时,都判断一下timerId是否存在即可。存在则不执行,否则立即执行。
一个简单例子(这里的this可以取到调用环境的):
js
function throttle (fn, delay) {
let timerId = null;
return function (...args) {
if (timerId) {
return;
}
fn.apply(this, args)
timerId = setTimeout(() => {
timerId = null;
}, delay)
}
}
带尾执行的throttle
错误改装示例
带尾执行如果基于上一个例子改装,就会遇到问题:按一下,也会执行两次
。
js
// 错误示例
function throttle (fn, delay, tail) {
let timerId = null;
return function (...args) {
if (timerId) {
return;
}
fn.apply(this, args)
timerId = setTimeout(() => {
if (tail) {
fn.apply(this, args)
}
timerId = null;
}, delay)
}
}
正确尾执行
正确尾执行的时机,是:连续两次按下间隔内按下了
。
如何判断呢?我们可以用一个变量剩余时间
来判断。
txt
两次按下间隔内 = 剩余时间 > 0
按下完全停止 = 剩余时间 <= 0
所以,按下时剩余时间小于等于0,是完全停止的,会直接触发运行。
按下时剩余时间大于0,说明已经处于按的过程中,这时候我们添加尾执行计时器,最终在下一次结束时运行。
这样,每次第一次按下只会执行一次,两次间隔内按下
会触发尾执行。
(1)如果第一次快结束前,又按下,则不会触发。剩余时间 > 0,正处于尾执行计时器运行过程中。
(2)如果第一次结束后,又按下。剩余时间 <= 0,已经完全停止,此时直接运行。
下面是代码实现:
js
function throttle(fn, delay, tail) {
let timerId = null;
let lastRunTime = 0;
return function (...args) {
const that = this;
function run() {
fn.apply(that, args);
lastRunTime = Date.now();
if (timerId) {
clearTimeout(timerId);
timerId = null;
}
}
// 剩余间隔时间
let remain = delay - (Date.now() - lastRunTime);
// 如果剩余时间 < 0,则说明运行停止,立即重新运行,并清除尾调用计时器
if (remain <= 0) {
run();
return;
}
// 如果有剩余间隔时间,说明在运行中,则触发尾调用setTimeout
// 尾调用
if (tail) {
if (timerId) {
return;
}
timerId = setTimeout(() => {
run();
}, remain);
}
};
}
测试代码(放在html中)
js
const dom = document.getElementById("input");
console.time("-->")
dom.oninput = throttle(() => {
console.timelog("-->")
},500,{ tail:true })