节流和防抖
1 句话概括:
- 节流: 持续不断的动作隔一段时间执行 1 次。
- 防抖: 持续不断的动作在停止动作一段时间后执行最后的 1 次动作。
场景模拟:
- 也可以把动作比作自动浇水的龙头,如果一天 24 小时都打开阀门浇水,这对它是不好的,为了花的更好生长,添加一个控制,间隔 12 小时共早晚浇两次,花朵能长得越来越好。
- 在监听页面滚动时,这个滚动的事件是不断在触发的,假设 1s 这个浏览器向我们报告 10 次,那么从滚动开始到停止的 n 秒内,我们对这个滚动事件就需要处理 10 * n 次,再加上对滚动报告时的处理,这无疑是非常大的开销。
- 囧豆上了年纪了,手总是不自觉的抖,女儿贴心地为他设置了一个一键拨打电话给自己的按钮,按下就可以直接打给自己。有一天囧豆拿起手机,按下按钮,但是因为手抖,其实按了 3 下,所以打出了 3 个电话(现实生活中不会打出 3 个电话的,这里是强行举例)。对于囧豆的 3 次按下按钮的动作,其实只需要 1 次生效即可,这是防抖对于此类场景的作用。
- 闪速是一个做什么超级迅速的男孩,今天他打开百度搜索页面,以光速输入了“为什么我的速度这么快”想要探知究竟。正当他输入完他的疑问,百度搜索框下方的智能提示缓缓地显示出“为什么”->“为什么会打呼噜”->“为什么英文怎么写”->“为什么我的眼里常含泪水,因为我对这片土地爱的深沉”->“为什么会打呼噜”->“为什么我的眼里常含泪水全诗”->“为什么我的眼里常含泪水全诗”->“为什么我的速腾螺丝好像都被拧过”…看着以龟速慢慢变化的智能提示,他明白了百度能解答他的疑惑。闪速输入的每一个字,百度都在预想他想问的问题,但是闪速是在是太快了,以至于他输入完了问题,百度还没有反应过来。当百度给问题输入加上了防抖,0.5s 内预想一次,这时闪速再次输入问题搜索,直接打开了他的问题搜索结果,闪速满意地笑了。
代码实现:
1 | function throttle(func, limit) { |
过程分析:(假设 limit = 1000ms, 初始的 Date.now() = 0, 事件每隔 100ms 触发一次)
- 定义变量 定时器任务lastFunc、上次运行时间lastRan,此时
Date.now() = 0
,remain = limit - 当前时间戳(0) 即 remain = 1000 - 0 = 0
,此时remain < 0
,执行一次回调,并且标记lastRan = 当前时间(0)
- 事件继续触发中,现在过去了 100ms, 那么此时的
Date.now() = 100
,remain = 1000 - (100 - 0) = 900
,意为距离下一次执行回调还需 900ms,这时进入 else if 分支,lastFunc
定时器此时没有任务,所以继续执行,设定 lastFunc 定时器为 900ms 后如果距离上一次执行回调的时间大于 limit,则执行回调并且标记lastRan
- 事件继续触发中,现在过去了 200ms, 那么此时的
Date.now() = 200
,remain = 1000 - (200 - 0) = 900
,意为距离下一次执行回调还需 800ms,这时进入 else if 分支,lastFunc
定时器此时已经有了任务,任务执行时间(100 + 900)
和现在200 + 800 = 1000
相同,无需重复设置定时任务,本次事件不做处理 - 在接下来的
300ms - 1000ms
内,都在重复 3 的步骤 - 继续触发,此时的
Date.now() = 1000
,remain = 1000 - (1000 - 0) = 0
,进入remain <= 0
分支,这时检查是否有定时器任务,将它清除(因为这时需要马上执行回调,不需要等待),执行回调并且标记lastRan = 1000
- 继续触发,此时的
Date.now() = 1100
,remain = 1000 - (1100 - 1000) = 900
,又回到了 2 步骤的处理过程,如果此时事件停止触发,那么定时器任务将会在 900ms 后执行最后一次回调
1 | function debounce(fn, interval = 1000) { |
过程分析:用户开始输入,噼里啪啦在触发中 input 事件中,每一次 input 会重置之前的定时器任务,一直到停止输入的一段时间(这段时间>=interval)时,此时定时器还有任务没有被清除,执行回调,over。
完结,撒花。