节流防抖适合的场景
- 多次点击发送验证码按钮,多次点击提交按钮,多次点击支付按钮等情况
- 监听了resize、scroll等事件,触发频率太高
- 类似百度那种input搜索框输入关键字实时显示联想的热门关键词
说白了就是限制函数的执行频率,减少不必要的资源、接口请求或者dom操作
两者比较
节流:单位时间内函数只执行一次(函数多次触发只执行第一次)
防抖:单位时间内函数多次触发都会被重置,延迟一段时间过后执行一次(函数多次触发只会延时执行最后一次)
节流实现
方法一:时间戳
function throttle(fn, delay) {
let previous = 0 // 上一次触发的时间戳, 初始为0, 确保第一次触发产生回调
return (...args) => {
let now = Date.now() // 当前触发时的时间戳
let that = this
if (now - previous > delay) { // 如果时间差大于规定时间, 则触发函数
fn.call(that, ...args)
previous = now // 更新上一次触发时间
}
}
}
方法二:定时器
function throttle (fn, delay) {
let timer
return (...args) => {
if (timer) return // 若存在定时器,说明已有函数正在等待执行,直接返回,不触发重复执行
timer = setTimeout(() => {
fn.call(this, ...args)
timer = null
}, delay)
}
}
防抖实现
一般通过清除timer定时器对象来实现
function debounce(fn, delay) {
let timer = null // 声明一个变量存储定时器
return (...args) => {
timer && clearTimeout(timer) // 重复触发,清除定时器
let that = this
timer = setTimeout(() => { // 重新定义定时器,重新开始计时
fn.call(that, ...args)
}, delay)
}
}
上面的代码也算实现了一个防抖函数,但是有些场景可能会有问题。假设我们给支付按钮设置了防抖,设置了delay为500ms,当用户不断重复点击支付按钮时,可能点击间隔小于500ms,这样就会导致用户一直在点击,定时器一直在重置,页面没有任何反应,用户可能以为页面卡死。所以这个时候我们就需要第一次点击支付按钮的时候,先立即执行一次,后续的点击按照delay延迟执行
// immediate:是否第一次点击立即执行
function debounce(fn, delay, immediate) {
let timer = null
let flag = false // flag变量用来记录是否执行过第一次,默认false
return (args) => {
timer && clearTimeout(timer) // 先清除定时器
// 需要立即执行,且是第一次触发
if (immediate && !flag) {
fn.apply(this, args) // 立即执行
flag = true // 将flag设置为true,这样后面频繁触发会去设置定时器
return
}
timer = setTimeout(() => {
fn.apply(this, args)
flag = false // 将flag设为false,这样下一次的第一次触发也可以被立即执行
}, delay)
}
}