函数节流(throttle)的实现
What is 节流?
为了限制函数一段时间内只能执行一次。 通过使用定时任务,延时方法执行。 在延时的时间内,方法若被触发,则直接退出方法。 从而实现一段时间内只执行一次。
执行过程
- 当事件触发时,相应的函数不会立即触发,而是会等待一定的时间
当事件密集触发时,函数的触发将会在间隔指定时间后触发一次
09d59b2ae785070fbe1b8a8fac6e14de应用场景
- scroll 事件,每隔一秒计算一次位置信息等
- 浏览器播放事件,每个一秒计算一次进度信息等
- input 框实时搜索并发送请求展示下拉列表,每隔一秒发送一次请求 (防抖也可)
实现
简易实现
function throttle(func, interval) {
//记录上一次开始时间
let lastTime = 0;
return function _throttle(...args) {
//获取当前事件触发的时间
const nowTime = new Date().getTime();
//计算还需要多长时间才触发函数
//给定的时间间隔减去当前时间与上次开始时间之差
const remainTime = interval - (nowTime - lastTime);
if (remainTime <= 0) {
func.apply(args);
lastTime = nowTime;
}
};
}
简易版实现了最基本的节流函数,但是它有个缺陷,在第一次触发节流函数时,他的回调总是会立即执行一次,原因是在计算remainTime时,由于lastTime为0,导致计算结果远小于0。
功能新增(立即执行)
function throttle(
func,
interval,
options = { immediate: true }
) {
//记录上一次开始时间
let lastTime = 0;
const { immediate } = options;
return function _throttle(...args) {
//获取当前事件触发的时间
const nowTime = new Date().getTime();
//只有当第一次执行的时候会被触发
if (!lastTime && !immediate) lastTime = nowTime;
//计算还需要多长时间才触发函数
//给定的时间间隔减去当前时间与上次开始时间之差
const remainTime = interval - (nowTime - lastTime);
if (remainTime <= 0) {
func.apply(args);
lastTime = nowTime;
}
};
}
功能新增(尾随)
function throttle(
func,
interval,
options = { immediate: true, trailing: true }
) {
//记录上一次开始时间
let lastTime = 0;
let timer = null;
const { immediate, trailing } = options;
return function _throttle(...args) {
//获取当前事件触发的时间
const nowTime = new Date().getTime();
if (!lastTime && !immediate) lastTime = nowTime;
//计算还需要多长时间才触发函数
//给定的时间间隔减去当前时间与上次开始时间之差
const remainTime = interval - (nowTime - lastTime);
if (remainTime <= 0) {
clearTimeout(timer);
timer = null;
func.apply(args);
lastTime = nowTime;
return
}
if (trailing && !timer) {
timer = setTimeout(() => {
timer = null
func.apply(args);
//如果是立即执行,那么在间隔内第二次触发时会进入该延时回调
//例如在第1s时再次触发,那么延时回调会在1s后回调,此时如果用户在2.1s时再进入节流函数,此定时器已经在2s时被执行了,lastTime置0会导致调用两次的bug
//解决方法:如果是立即执行,那么在触发该回调时将当前时间给lastTime 让上面的函数不执行
//解决方法:如果不是立即执行,那么将其置0 让上面的函数不执行
lastTime = !immediate ? 0 : new Date().getTime();
}, remainTime);
}
};
}
功能新增(获取func返回值)
- 使用Promise
function throttle(
func,
interval,
options = { immediate: true, trailing: true }
) {
//记录上一次开始时间
let lastTime = 0;
let timer = null;
const { immediate, trailing } = options;
return function _throttle(...args) {
return new Promise(function (resolve, reject) {
//获取当前事件触发的时间
const nowTime = new Date().getTime();
if (!lastTime && !immediate) lastTime = nowTime;
//计算还需要多长时间才触发函数
//给定的时间间隔减去当前时间与上次开始时间之差
const remainTime = interval - (nowTime - lastTime);
if (remainTime <= 0) {
clearTimeout(timer);
timer = null;
const result = func.apply(args);
resolve(result);
lastTime = nowTime;
return;
}
if (trailing && !timer) {
timer = setTimeout(() => {
timer = null;
const result = func.apply(args);
resolve(result);
lastTime = !immediate ? 0 : new Date().getTime();
}, remainTime);
}
});
};
}