/** * 返回一个自带消抖效果的函数,下文用fnDebounced表示。 * * fn: 需要被消抖的函数 * delay: 消抖时长 * isImmediateCall: 是否在一组操作中的第一次调用时立即执行fn * isRememberLastCall:是否在一组中最后一次调用后等delay时长再执行fn * * 如果isRememberLastCall为false,意味着fn不会被延迟执行,所以fnDebounced执行时,要么在内部调用fn,同步返回fn返回值;要么内部决定本次不调用fn,同步返回null。 * 如果isRememberLastCall为true,意味着fn可能被延迟执行,所以fnDebounced会返回一个Promise,在fn被调用时用其返回值resolve该Promise,或者在fn的延时调用计划被取消时用'canceled'resolve该Promise。(不宜reject,否则又没有人去catch,会导致浏览器报错。) */ export function debounce(fn, delay = 250, isImmediateCall = false, isRememberLastCall = true) { console.assert(isImmediateCall || isRememberLastCall, 'isImmediateCall 和 isRememberLastCall 至少应有一个是true,否则没有意义!') let timer = null let retPromiseLastTimeResolver = null // 上次调用的时刻 let lastCallTime = 0 if (isImmediateCall && !isRememberLastCall) { return function (...args) { let ret = null const currentTime = Date.now() if (currentTime - lastCallTime >= delay) { ret = fn.apply(this, args) } lastCallTime = currentTime return ret } } else if (!isImmediateCall && isRememberLastCall) { return function (...args) { if (timer) { clearTimeout(timer) timer = null } if (retPromiseLastTimeResolver) { retPromiseLastTimeResolver('canceled') retPromiseLastTimeResolver = null } const ret = new Promise((resolve, reject) => { retPromiseLastTimeResolver = resolve timer = setTimeout(() => { timer = null retPromiseLastTimeResolver = null resolve(fn.apply(this, args)) }, delay) }) return ret } } else if (isImmediateCall && isRememberLastCall) { return function (...args) { const currentTime = Date.now() if (currentTime - lastCallTime >= delay) { // 一组操作中的第一次 const res = fn.apply(this, args) lastCallTime = currentTime return Promise.resolve(res) } else { // 一组中的后续调用 if (timer) { // 在此之前存在中间调用 lastCallTime = currentTime clearTimeout(timer) timer = null } if (retPromiseLastTimeResolver) { retPromiseLastTimeResolver('canceled') retPromiseLastTimeResolver = null } const ret = new Promise((resolve, reject) => { retPromiseLastTimeResolver = resolve timer = setTimeout(() => { lastCallTime = 0 timer = null retPromiseLastTimeResolver = null resolve(fn.apply(this, args)) }, delay) }) return ret } } } else { console.error('不应该执行到这里!') } } export default { debounce, }