防抖和节流的简单理解
节流相当于技能cd(一段时间内只能点击一次),
防抖相当于回城(你打断我我姐重新计时回城)。
一、防抖(debounce)
防抖,顾名思义,防止抖动,以免把一次事件误认为多次,敲键盘就是一个每天都会接触到的防抖操作。
1.1、原理
1)某个任务你并不想它太过频繁触发,那么设置一个指定间隔的定时器来延迟执行;
2)每次进来的时候都清除原本的定时器,然后重新开始计时;
3)只有任务触发的间隔超过指定间隔的时候,任务才会执行。
1.2、特点
1)如果在指定间隔内(如 1000ms)再次触发任务,那么当前的计时取消,所有任务不会执行,重新开始计时。
2)如果在指定间隔内(如 1000ms)没有再次触发任务,那么就执行最后一次任务
也就是说,如果触发太过频繁,会导致一次响应都没有。只能等你最后一次触发结束指定间隔(如 1000ms)后,才能执行最后一次任务。
1.3、应用场景
1)登录、发短信等按钮避免用户点击太快,以致于发送了多次请求,需要防抖
2)调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖
3)文本编辑器实时保存,当无任何更改操作一秒后进行保存
1.4、代码实现
/**
* 防抖
* @param {String} fn 回调方法
* @param {String} delay 缓冲时间
*/
function debounce(fn, delay) {
// 创建一个标记用来存放定时器
let timeout = null
return function () {
// 每次函数触发的时候,清空之前的定时器
clearTimeout(timeout)
// 创建一个新的 setTimeout
// 这样就能保证点击按钮后的 delay 时间间隔内
// 如果用户还点击了的话,就不会执行 fn 函数
timeout = setTimeout(() => {
// 使用apply修正 this 指向,执行传入函数
fn.apply(this, arguments)
}, delay)
}
}
二、节流 (throttle)
节流,顾名思义,控制水的流量。控制事件发生的频率,如控制为 1s 发生一次,甚至 1 分钟发生一次。与服务端(server)及网关(gateway)控制的限流 (Rate Limit) 类似。
2.1、原理
1)初始化一个开关锁为真
2)设置闭包函数,函数内判断标记为真才执行,然后将标记置为假
3)闭包函数内一个指定间隔的定时器来延迟执行任务,执行完则再将标记置真;
2.2、特点
1)无论在指定间隔内(如 1000ms)触发多少次任务,只执行第一次任务
2)在触发任务的指定间隔(如 1000ms)之后,肯定执行第一次任务。
也就是说,只要你触发一次任务,每在指定间隔(如 1000ms)之后肯定执行且只执行第一次;循环往复。
2.3、应用场景
1)scroll 事件,每隔一秒计算一次位置信息等
2)浏览器播放事件,每个一秒计算一次进度信息等
3)input 框实时搜索并发送请求展示下拉列表,每隔一秒发送一次请求 (也可做防抖)
2.4、代码实现
/**
* 节流
* @param {String} fn 回调方法
* @param {String} delay 缓冲时间
*/
function throttle(fn, delay) {
// 初始化一个状态为真
let canRun = true
return function () {
// 判断状态,休息时间 暂不接客
if (!canRun) {
return
}
// 工作时间,执行函数;
// 在间隔期内把状态位设为假
canRun = false
// 创建定时器,延迟执行任务
setTimeout(() => {
// 使用apply修正 this 指向,执行传入函数
fn.apply(this, arguments)
// 执行完任务之后,重新将这个标志设置为真
canRun = true
}, delay)
}
}
三、总结
附演示代码
<div>
<button id="debounceBtn">点我防抖</button>
<button id="throttleBtn">点我节流</button>
</div>
<script>
// 点击按钮次数
let clickNum = 0
// 按钮点击时间
let clickTime = 0
// 按钮开始点击时间
let startTime = 0
// 执行时间
let executionTime = 0
// 获取按钮
const debounceBtn = document.getElementById('debounceBtn')
const throttleBtn = document.getElementById('throttleBtn')
// 绑定点击事件
debounceBtn.onclick = function (e) {
clickBtn()
debounceFn(clickNum)
}
throttleBtn.onclick = function (e) {
clickBtn()
throttleFn(clickNum)
}
// 节流及防抖事件
let debounceFn = debounce(fnExecution, 1000)
let throttleFn = throttle(fnExecution, 1000)
// 点击按钮事件
function clickBtn() {
clickNum++
clickTime = Date.now()
if (clickNum === 1) {
startTime = Date.now()
}
console.log(`这是第${clickNum}次点击`)
}
function fnExecution(num) {
executionTime = Date.now()
console.log(
`生效的是第 ${num}次点击,生效时间与最后一次点击隔时间为${
executionTime - clickTime
}毫秒,生效时间与第一次点击隔时间为${executionTime - startTime}毫秒`
)
clickNum = 0
}
/**
* 防抖
* @param {String} fn 回调方法
* @param {String} delay 缓冲时间
*/
function debounce(fn, delay) {
// 创建一个标记用来存放定时器
let timeout = null
return function () {
// 每次函数触发的时候,清空之前的定时器
clearTimeout(timeout)
// 创建一个新的 setTimeout
// 这样就能保证点击按钮后的 delay 时间间隔内
// 如果用户还点击了的话,就不会执行 fn 函数
timeout = setTimeout(() => {
// 使用apply修正 this 指向,执行传入函数
fn.apply(this, arguments)
}, delay)
}
}
/**
* 节流
* @param {String} fn 回调方法
* @param {String} delay 缓冲时间
*/
function throttle(fn, delay) {
// 初始化一个状态为真
let canRun = true
return function () {
// 判断状态,休息时间 暂不接客
if (!canRun) {
return
}
// 工作时间,执行函数;
// 在间隔期内把状态位设为假
canRun = false
// 创建定时器,延迟执行任务
setTimeout(() => {
// 使用apply修正 this 指向,执行传入函数
fn.apply(this, arguments)
// 执行完任务之后,重新将这个标志设置为真
canRun = true
}, delay)
}
}
</script>
四、插件lodash实现防抖和节流(推荐)
lodash插件:里面封装函数的防抖与节流与节流的业务
【闭包+延迟器】
lodash函数库对外暴露_
函数
如果没有可以安装一下
npm i --save lodash
4.1、使用
import _ from 'lodash'
语法
fn是你需要处理的方法,delay延迟时间
_.throttle(fn,delay)
_.debounce(fn,delay)
网友评论