节流函数
相对于防抖函数,节流函数可以说是复杂一些。但也不是不能写,就是思维需要更严谨那么一点点。不懂防抖可以去看看我的上一篇文章。本篇主要进行节流函数的实现。可以指定函数第一次是否执行,最后一次是否执行,也可以取消,也可以通过promise拿到函数执行结果,也可以通过回调函数的方式拿到执行结果。
/**
*
* @param {*} fn 需要节流的函数
* @param {*} interval 间隔时间
* @param {*} options 参数
* { leading: true, 头部(也就是第一次)是否执行 默认执行
* trailing: true, 尾部(也就是最后一次触发)是否执行 默认执行
* successCallback: undefined, 成功回调
* failCallback: undefined 失败回调 可以用来拿到执行结果
* }
* @returns
*/
function throttle(fn, interval, options = { leading: true, trailing: true, successCallback: undefined, failCallback: undefined }) {
// 上次节流函数执行的时间
let lastTime = 0;
// 定时器 最后一次触发事件时需要
let timer = null;
const { leading, trailing, successCallback, failCallback } = options;
function _throttle(event) {
return new Promise((resolve, reject) => {
// 获取当前的时间戳
const nowTime = new Date().getTime();
// 第一次是否触发的条件 满足条件 则第一次事件发生不触发
if (lastTime === 0 && leading === false) lastTime = nowTime;
// 判断当前的执行时间和上次执行时间的时间差 是否满足间隔时间差
// 也就是计算一下剩余时间 如果小于等于0 则满足可再次执行条件
const remainTime = interval - (nowTime - lastTime);
if (remainTime <= 0) {
// 定时器存在的话 直接取消 因为我现在已经要执行函数了
if (timer) {
clearTimeout(timer);
timer = null;
}
// 执行 并可以拿到返回值结果
try {
const res = fn.call(this, event);
if (typeof successCallback === "function") successCallback(res);
resolve(res);
} catch (error) {
if (typeof failCallback === "function") failCallback(error);
reject(error);
}
// 更新上次函数的执行时间
lastTime = nowTime;
}
// 需要执行最后一次
else if (trailing && !timer) {// 定时器是不需要每次更新的 没什么必要
timer = setTimeout(() => {
// 执行函数 也需要清空当前使用了的定时器
timer = null;
// TODO 难点:最后一次函数执行时间的重置 第一次如果需要执行,将时间置为当前最新的时间 否则置为0
lastTime = !leading ? 0 : new Date().getTime();
// 执行 并可以拿到返回值结果
try {
const res = fn.call(this, event);
if (typeof successCallback === "function") successCallback(res);
resolve(res);
} catch (error) {
if (typeof failCallback === "function") failCallback(error);
reject(error);
}
}, remainTime);
}
});
}
// 取消功能
_throttle.cancel = function () {
if (timer) {
clearTimeout(timer);
timer = null;
lastTime = 0;
}
}
return _throttle;
}