清理所有定时器
export function clearAllTimer() {
let id = setTimeout(() => { }, 0)
while (id > 0) {
window.clearTimeout(id)
id--
}
}
防抖代码
当函数绑定一些持续触发的事件如:resize、scroll、mousemove ,键盘输入,多次快速click等等,
如果每次触发都要执行一次函数,会带来性能下降,资源请求太频繁等问题
// 第一个参数是需要进行防抖处理的函数,第二个参数是延迟时间,默认为1秒钟
// 这里多传一个参数,immediate用来决定是否要第一次立即执行, 默认为false
function debounce(fn, delay = 1000, immediate = false, resultCb) {
// 实现防抖函数的核心是使用setTimeout
// time变量用于保存setTimeout返回的Id
let time = null
// isImmediateInvoke变量用来记录是否立即执行, 默认为false
let isImmediateInvoke = false
// 将回调接收的参数保存到args数组中
function _debounce(...args) {
// 如果time不为0,也就是说有定时器存在,将该定时器清除
if (time !== null) {
clearTimeout(time)
}
// 当是第一次触发,并且需要触发第一次事件
if (!isImmediateInvoke && immediate) {
// 将函数的返回值保存到result中
const result = fn.apply(this, args)
if (typeof resultCb === 'function') {
// 当用户传递了resultCb函数时,执行该函数,并将结果以参数传递出去。
resultCb(result)
}
// 将isImmediateInvoke设置为true,这样不会影响到后面频繁触发的函数调用
isImmediateInvoke = true;
}
time = setTimeout(() => {
// 使用apply改变fn的this,同时将参数传递给fn
fn.apply(this, args)
// 当定时器里的函数执行时,也就是说是频繁触发事件的最后一次事件
// 将isImmediateInvoke设置为false,这样下一次的第一次触发事件才能被立即执行
isImmediateInvoke = false
}, delay)
}
// 防抖函数会返回另一个函数,该函数才是真正被调用的函数
return _debounce
}
使用
const a =debounce(()=>{console.log('asd')})
button.onclick(a)
button被点击的事件就被加上防抖函数了。
节流代码
限制代码访问频率,如某个代码1分钟只能执行一次。
自定义
时间戳
function throttle(func, wait) {
var previous = 0;
return function() {
let now = Date.now();
let context = this;
let args = arguments;
if (now - previous > wait) {
func.apply(context, args);
previous = now;
}
}
}
const throttledFn = throttle(count,1000);
定时器
function throttle(func, wait) {
let timeout;
return function() {
let context = this;
let args = arguments;
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
func.apply(context, args)
}, wait)
}
}
}
复杂实现
// leading参数用来控制是否第一次立即执行,默认为true
function throttle(fn, interval, leading = true) {
//该变量用于记录上一次函数的执行事件
let lastTime = 0
// 内部的控制是否立即执行的变量
let isLeading = true
// time 保存定时器的id
let time = null
const _throttle = function(...args) {
// 获取当前时间
const nowTime = new Date().getTime()
// 第一次不需要立即执行
if (!leading && isLeading) {
// 将lastTime设置为nowTime,这样就不会导致第一次时remainTime大于interval
lastTime = nowTime
// 将isLeading设置为false,这样就才不会对后续的lastTime产生影响。
isLeading = false
}
// cd剩余时间
const remainTime = nowTime - lastTime
// 如果剩余时间大于间隔时间,也就是说可以再次执行函数
if (remainTime - interval >= 0) {
fn.apply(this, args)
// 将上一次函数执行的时间设置为nowTime,这样下次才能重新进入cd
lastTime = nowTime
}
if (remainTime < interval) {
// 判断是否存在定时器,如果存在则取消掉
if (time) clearTimeout(time)
// 设置定时器
time = setTimeout(() =>{
// 由于该定时器,会在没有事件触发的interval时间间隔后才会执行,也就是说一轮事件
// 执行已经结束,使用需要将isLeading复原,这样下一轮事件的第一次事件就不会立即执行了。
isLeading = true
}, interval)
}
}
// 返回_throttle函数
return _throttle
}
vueuse中工具类
const throttledFn = useThrottleFn(() => {
// 操作,1s只能执行一次
}, 1000)
定时任务
手动实现
setInterval(() => {
handleQuery();
}, 1000 * 60 * 5);
setInterval不晓得为啥有时候有bug
// 定时器查询
function handleQueryByInterval() {
setTimeout(() => {
handleQuery();
handleQueryByInterval();
}, 1000 * 60 * 5);
}
工具类useInterval
useInterval(() => {
handleQuery();
}, 1000);