在实际的开发中,我们通常会期望在首次和末次都执行一次,这时候我们就需要将节流和防抖结合起来。
throttleAndDebounce
function throttleAndDebounce(fn: () => void, delay: number) {
let timeout: number
let called: boolean
let ctx: any
return function(...args: []) {
ctx = this
if (timeout) {
clearTimeout(timeout)
}
if (!called) {
fn.apply(ctx, args)
called = true
timeout = setTimeout(() => {
called = false
}, delay)
} else {
timeout = setTimeout(() => fn.apply(ctx, args), delay)
}
}
}
为了实现对应的效果,我们需要使用闭包来保存两个参数,定时器的 timeout
id 和当前的执行状态 called
。当执行函数的时候,每次都会检查是否存在 timeout
id,若存在则将其清理掉。然后再判断当前的执行状态,当在当前周期内执行过一次之后就将 called
置为 true,并生成一个计时器,在特定时间之后再将其重置为 false。若在某个时间停止执行之后,还会保留一个计时器,即 16 行所写,这样就会在最后一次触发特定时间之后完成最后的触发操作。
debounce
防抖是指对持续触发的函数进行延迟执行,当触发之后间隔一点时间没有触发时函数才会执行一次。因为这样的特性,通常情况下第一次触发不会直接执行,而最后一次触发会在间隔一定时间之后进行执行。
function debounce(fn: () => void, delay: number) {
let timeout: number
return function (...args) {
const ctx = this
clearTimeout(timeout)
timeout = setTimeout(() => {
fn.apply(ctx, args)
})
}
}
function debounce(fn: Function, wait: number, immediate: boolean) {
let timeout
return function (...args: any[]) {
const ctx = this
const callNow = immediate && !timeout
const later = function () {
timeout = null
if (!immediate) fn.apply(ctx, args)
}
clearTimeout(timeout)
timeout = setTimeout(later, wait)
if (callNow) fn.apply(ctx, args)
}
}
函数会在第一次触发的时候执行,间隔一定时间之后,再次触发也是最后先执行。
throttle
节流是当函数不断进行触发的时候,只有在经过特定的时间间隔才会执行一次。
function throttle(fn: () => void, delay: number) {
let lastTime = 0
return function (...args) {
let now = +new Date()
if (now - lastTime > delay) {
lastTime = now
fn.apply(this, args)
}
}
}
function throttle(fn: Function, delay: number) {
let timerId = null
return function (...args) {
const ctx = this
if (!timerId) {
timerId = setTimeout(() => {
timerId = null
fn.apply(ctx, args)
}, delay)
}
}
}
节流是第一次就会触发,然后再在固定时间内触发一次,当停止之后最后一次也不会触发。