一、防抖
频繁调用一个函数时,只执行最后一次函数调用。如何判断是最后一次,即一个函数调用完后的一段时间内不再调用则为最后一次函数调用。
/**
* 当事件触发时,相应的函数并不会立即触发,而是会等待一定的时间;
* 当事件密集触发时,函数的触发会被频繁的推迟;
* 只有等待了一段时间也没有事件触发,才会真正的执行响应函数;
* 功能:基本实现/this和参数绑定/取消/第一次立即执行/获取回调返回值
* @param {*} callback 需要频繁执行的函数
* @param {*} delay 设置延时时间
* @param {*} immediate 设置是否首次立即执行
* @returns {function}
*/
export function debounce(callback, delay, immediate = false){
// 计时器
let timer = null
// 每轮循环首次立即调用 判断
let isInvoke = false
const _debounce = function(...args){
// 通过promise获取函数调用的返回值
return new Promise((resolve, reject) => {
try {
// 在计时器时间内再次调用取消定时
if(timer) clearTimeout(timer)
// 每个循环首次立即调用
if(immediate && !isInvoke){
resolve(callback.apply(this, args))
isInvoke = true
}
// 重新定时
timer = setTimeout(() => {
resolve(callback.apply(this, args))
init()
}, delay)
} catch (error) {
reject(error)
}
})
}
// 用于取消最后一次函数调用
const cancel = function() {
clearTimeout(timer)
init()
}
// 每个循环结束需要重制初始化状态
const init = function() {
timer = null
isInvoke = false
}
_debounce.cancel = cancel
return _debounce
}
/**
* 基本实现:频繁调用取最后一次。最后一次如何定义,在某个延迟时间没再调用函数则为最后一次
* 维护一个定时器 如果在定时器内调用函数 则上个定时器取消 重新开始计时
* @param {*} callback 要调用的函数
* @param {*} delay 延迟时间
* @returns
*/
function baseDebounce(callback, delay){
let timer = null
return function(...args){
if(timer) clearTimeout(timer)
timer = setTimeout(() => {
callback.apply(this, args)
timer = null
}, delay)
}
}
二、节流
频繁调用一个函数,在一个时间段内只允许调用一次,让函数调用控制在一定的频率内
/**
* 当事件触发时,会立即执行这个事件的响应函数;
* 如果这个事件会被频繁触发,那么节流函数会按照一定的频率来执行函数;
* 不管在这个中间有多少次触发这个事件,执行函数的频繁总是固定的;
* 基本实现/this和参数/是否立即执行/最后一次是否执行/取消/返回值
* @param {*} callback
* @param {*} interval
*/
export function throttle(callback, interval, {leading = true, trailing = false} = {}){
// 间隔开始时间
let startTime = 0
// 最后一次函数调用计时器
let timer = null
const _throttle = function(...args){
return new Promise((resolve, reject) => {
try {
// 当前函数调用时间
const currentTime = new Date().getTime()
// 如果首次不执行 则当前时间为开始时间 则当前时间一定在间隔内
if(!leading && startTime === 0) startTime = currentTime
// 距离间隔结束的时间 也用来定时最后一次执行函数
const remainTime = interval - (currentTime - startTime)
// 判断离间隔时间结束还有多久 负数则在间隔时间之外可以调用
if(remainTime <= 0) {
// 如果有定时器则必定不是最后一次调用
if(timer){
clearTimeout(timer)
timer = null
}
resolve(callback.apply(this, args))
// 更新间隔开始时间
startTime = currentTime
return
}
// 最后一次需要执行则需保存每次函数调用 可能为最后一次也可能作为下一个时间间隔开始
if(trailing){
// 取消上一个可能的最后一次函数执行
if(timer) clearTimeout(timer)
// 设置这次为可能的最后一次函数执行
timer = setTimeout(() => {
resolve(callback.apply(this, args))
// 下一个间隔开始时间
startTime = new Date().getTime()
timer = null
}, remainTime)
}
} catch (error) {
reject(error)
}
})
}
// 如果开启尾部执行功能则可以取消最后一次
function cancel(){
if(trailing && timer) {
clearTimeout(timer)
}
timer = null
startTime = 0
}
_throttle.cancel = cancel
return _throttle
}
/**
* 基本实现:在某个时间段内,函数只会被调用一次
* 维护一个时间间隔,首次调用为开始时间,间隔内再次调用函数无效
* 时间间隔外首次调用为下一次间隔开始时间
* @param {*} callback
* @param {*} interval
* @returns
*/
function baseThrottle(callback, interval){
let startTime = 0
const _throttle = function(...args){
const currentTime = new Date().getTime()
const remainTime = interval - (currentTime - startTime)
if(remainTime <= 0) {
callback.apply(this, args)
startTime = currentTime
}
}
return _throttle
}