防抖(debounce)和节流(throttle)
debounce英 [di'bauns] throttle英 [ˈθrɒtl]
本质上是优化高频率执行代码的一种手段
如:浏览器的 resize、scroll、keypress、mousemove 等事件在触发时,会不断地调用绑定在事件上的回调函数,极大地浪费资源,降低前端性能
为了优化体验,需要对这类事件进行调用次数的限制,对此我们就可以采用 防抖(debounce) 和 节流(throttle) 的方式来减少调用频率
定义
节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效
防抖: n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时
防抖
为什么会出现debounce和throttle
防抖和截流是针对响应跟不上触发频率这类问题的两种解决方案。
在给DOM绑定事件时,有些事件我们时无法控制触发频率的。如鼠标移动事件onmousemove,滚动滚动条事件onscroll,窗口大小改变事件onresize,瞬间的操作都会导致这些事件会被高频触发。如果事件的回调函数较为复杂,就会导致响应跟不上触发,出现页面卡顿,假死现象。在实时检查输入时,如果我们绑定onkeyup事件发请求去服务器端检查,用户输入过程中,事件的触发频率也会很高,会导致大量的请求发出,响应速度会大大跟不上触发
总结:以下场景往往由于事件频繁被触发,而重复执行DOM操作,资源加载等重行为、会导致UI停顿甚至浏览器崩溃。
window对象的resize、scroll和拖拽时的mousemove事件
射击游戏中的mousedown,keydown事件
类似百度搜索提示keyup事件
问题分析
DOM操作比非DOM交互需要更多的内存和CPU事件,连续尝试进行过多的DOM相关操作可能会导致浏览器挂起,有时候甚至会崩溃
实际上对于window的resize事件,实际需求大多为停止改变大小n毫秒后执行后续操作、而拖拽的mousemove事件函数则是以一定的频率执行后续处理。比如拖拽事件、可以在用户能够接受的范围内更改代码的执行频率、mousemove的触发频率大概为10ms【100次】一次,而当我们设置执行事件为50ms【20次】一次时,用户也不会察觉、这样我们就可以在不影响用户体验的前提下,提高性能。
针对这两种需求就出现了防抖(debounce)和节流(throttle)两种解决方法
debounce防抖
debounce,去抖动。策略是当事件被触发时,设定一个周期延迟动作,若期间又被触发,则重新设定周期,直到周期结束,执行动作。这是debounce的基本思想,在后期又扩展了前缘debounce,即执行动作在前,然后设定周期,周期内有事件被触发,不执行动作,且周期重新设定
debounce的特点是当事件快速联系不断触发时,动作只会执行一次。延迟debounce,是在周期结束时执行。但当触发有间断,且简短大雨我们设定的时间间隔时,动作就会有多次执行
总结:什么是debounce,高频率触发的事件,我们最终只让他执行一次
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
#common {
position: absolute;
left: 0;
top: 0;
border: 1px solid #000;
width: 300px;
height: 600px;
}
#special {
position: absolute;
left:400px;
top: 0;
border: 1px solid #000;
width: 300px;
height: 600px;
}
</style>
</head>
<body>
节流代码
<div id="common">
throttle
</div>
<div id="special">
debounce
</div>
<script>
var delay = 300;
var common = document.getElementById('common') // 获取页面的左边
var special = document.getElementById('special') // 获取页面的右边
function addlist() { // 监听事件的响应事件,执行dom操作。
special.innerHTML += '<li>k</li>'
}
function commonWay() { // 这是执行了普通的函数
common.innerHTML += '<li>k</li>'
}
// tottle的实现,也就是节流的实现,就是设置了一个一开始函数运行的时间戳进行执行
var startTime, timestamp, timer;
function throttle(fn, delay) {
// timestamp = +new Date()
timestamp = new Date().getTime(); // 时间戳 timestamp = 10:23:10 10:23:20 10:23:59 10:23:60
clearTimeout(timer)
if (!startTime) {
startTime = timestamp // startTime = 10:23:59
}
if (timestamp - startTime >= delay) {
fn() // 这里才会调用
startTime = timestamp
} else {
timer = setTimeout(function () {
fn()
}, delay)
}
}
// 防抖
function debounce(fn, delay) { // 定义一个debounce函数
clearTimeout(fn.timeid)
fn.timeid = setTimeout(function () {
fn()
}, delay)
}
// 调整窗口大小触发
window.addEventListener('resize', function () {
throttle(commonWay, 200); // 节流 100ms执行一次,1s执行了10次;
debounce(addlist, delay); // 防抖
})
</script>
</body>
</html>
面试题:
什么是防抖:在频繁触发某一个事件时,一段时间内不再触发该事件后才会去调用对应的回调函数,在设定间隔时间内如果下一次事件被触发, 那么就重新开始定时器,直到事件触发结束。
什么时候使用:防抖用于高频触发的事件
特点:高频触发事件处理程序,只执行他的最后一次
总结
以下场景往往由于事件频繁被触发,而重复执行DOM操作、资源加载等重行为、会导致UI停顿甚至浏览器崩溃。
1、window对象的resize、scroll和拖拽时的mousemove事件
2、射击游戏中的mousedown、keydown事件
3、类似百度搜索提示keyup事件
防抖: 在事件多次触发时,通过防抖,只执行最后一次事件处理程序
节流: 降低事件处理程序执行的频率。 比如,onmousemove事件如果一秒钟执行200次,通过节流可以只执行20次