函数节流的背景、原理和实现
前言
开发中经常会遇到有些函数需要被频繁调用,但是太频繁又影响性能,那怎么办呢?这个时候就需要用到函数节流了。
什么是函数节流?函数节流就是指将函数的调用时间间隔控制为 >=t。
函数节流适合需要频繁调用,但又要限制调用频率的场景:
- mouseover / keydown
- DOM 拖拽 等等
实现
函数节流有两种实现方式:
- 与上一次执行时间做对比
- setTimeout 预约执行时间
下面具体讲讲两种方式的具体实现以及背后的一些原理和逻辑
方式1:与上一次执行时间做对比
先上代码
function throttle(fn, wait) {
let previous = 0
return function () {
// 此处用到了 + 运算符,后面再出一篇文章细讲该符号的一些其它用法
let now = +new Date()
if (now - previous < wait) return
fn.apply(this, arguments)
previous = now
}
}
这种实现方式比较直观,不作过多说明
方式2:setTimeout 预约执行时间
上代码
function throttle(fn, wait) {
let timeout = null
return function () {
if (timeout) return
let args = arguments
timeout = setTimeout(() => {
fn.apply(this, args)
timeout = null
}, wait)
}
}
先说一句setTimeout 的功能:在某个时间之后将函数加入事件循环队列。所以通过setTimeout 可以设置当前执行函数与下一函数执行间隔一定时间。
回到节流,当一直通过setTimeout 预约函数执行时,为保证预约函数执行间间隔t 时间,需要限制t 时间内不可预约,即预约函数时,设置标识,再在t 时刻执行函数时将标识取消
两种方式对比
- 时间戳方式:马上执行,停止触发后不执行
- setTimeout方式:非马上执行,停止触发后仍会执行一次
需求
同时满足:马上执行并且停止之后仍然执行一次。这个需求比较常遇到,在此做个分析。
先来一个大前提知识, 看下面代码:
document.getElementById('box').addEventListener('click', function () {
var time1 = setTimeout(function () {
console.log('time1')
clearTimeout(time2)
}, 1000)
var time2 = setTimeout(function () {
console.log('time2')
}, 1000)
})
点击box一秒之后输出:
time1
分析一下代码:点击box之后分别向定时器线程添加两个定时任务,1000ms之后均加入事件队列,先执行time1, 在time1中通过clearTimeout 将time2清除,然后time2 不执行。这个结果说明,加入事件队列的任务可以通过clearTimeout 清除掉。
回到需求,上代码:
function throttled(fn, wait) {
let previous = 0, timeout
return function () {
let now = +new Date()
let distance = now - previous
if (distance >= wait) {
// 除了最后一个时间段需要用预约函数,其他时间段均要清除掉
// 加入事件队列之后仍能清除,参照上面大前提分析
if (timeout) {
clearTimeout(timeout)
timeout = null
}
fn.apply(this, arguments)
previous = now
} else if (!timeout) {
// 此处实现停止之后再执行一次
// 时间段初始就预约时间段结束时的执行函数,时间段其他时刻不再预约
let remainTime = wait - distance
timeout = setTimeout(() => {
fn.apply(this, arguments)
timeout = null
previous = now
}, remainTime)
}
}
}
总结
本文介绍了节流的两种实现方式:"时间戳" 和 "setTimeout"。到此我们就介绍了函数频繁执行的两种处理方式:函数防抖 和 函数节流。其中函数防抖属于硬预约(不管是否已预约都要重新预约),函数节流属于软预约(已预约则不再预约)。
写得头疼,摸摸头上的头发,发现还在,欣慰欣慰!劝诫大家,趁头发还在就多摸摸,别等秃了望发空流泪。
Gitalking ...
Be the first guy leaving a comment!