@msom/common
Version:
@msom/common
155 lines (139 loc) • 4.64 kB
text/typescript
import { regressRange } from "../number";
import { CANCELED, OcPromise, PENDDING } from "../promise/OcPromise";
/**
* 防抖函数:延迟执行目标函数,在频繁触发时只执行最后一次
*
* @template T 目标函数的类型签名
* @param callable 需要防抖的目标函数
* @param {number | undefined} [wait=100] wait 等待时间(毫秒),默认 100ms
* @param options 配置选项
* @param {boolean | undefined} [options.leading=false] options.leading 前置执行
* @param {boolean | undefined} [options.trailing=true] options.trailing 后置执行
* @param {number | undefined} options.maxWait 如果超过maxWait等待时间,则调用时等同于前置执行
* @returns 经过防抖处理的新函数,并附加取消方法
*
* ### 特性
* 1. 支持 TypeScript 类型推断
* 2. 配置前置执行(leading)和后置执行(trailing)
* 3. 提供取消执行方法(cancel)
* 4. 自动管理函数上下文(this)和参数
* 5. 处理异步错误(通过 Promise 返回)
* 6. 边界值安全处理
*/
function debounce<
T extends (...args: any[]) => any,
Args extends Parameters<T> = Parameters<T>,
Returns extends ReturnType<T> = ReturnType<T>
>(
callable: T,
wait: number = 100,
options: {
leading?: boolean;
trailing?: boolean;
maxWait?: number;
} = {}
): (...args: Args) => OcPromise<Returns> {
// 参数校验
if (typeof callable !== "function") {
throw new TypeError("Expected a function");
}
if (typeof wait !== "number" || wait < 0) {
throw new TypeError("Wait must be a non-negative number");
}
// 配置处理
let {
leading = false, // 是否在前沿立即执行
trailing = true, // 是否在尾部延迟执行
maxWait, // 最大等待时间(类似节流)
} = options;
maxWait =
typeof maxWait === "number" ? regressRange(maxWait, [wait]) : maxWait;
// 状态变量
let timerId: ReturnType<typeof setTimeout> | null = null;
let lastCallTime: number | null = null;
let lastArgs: any[] | null = null;
let lastThis: any;
let lastResult: OcPromise<Returns>;
let pendingResolve: ((value: Returns) => void) | null = null;
// 清除定时器
const clearTimer = () => {
if (timerId) {
clearTimeout(timerId);
timerId = null;
}
};
// 执行目标函数
const invokeFunction = () => {
if (!lastArgs) return;
try {
const result = callable.apply(lastThis, lastArgs);
if (pendingResolve) {
pendingResolve(result);
pendingResolve = null;
}
return result;
} catch (error) {
// 保持错误传播
if (pendingResolve) {
pendingResolve(OcPromise.reject(error) as any);
pendingResolve = null;
}
throw error;
}
};
// 启动计时器
const startTimer = (pendingCallback: () => void, delay: number) => {
clearTimer();
timerId = setTimeout(pendingCallback, delay);
};
// 防抖主函数
const debounced = function (this: any, ...args: any[]) {
if (lastResult && lastResult.getStatus() === PENDDING) {
lastResult.cancel("reCall");
}
const now = Date.now();
lastArgs = args;
lastThis = this;
// 1. 首次调用处理
if (lastCallTime === null) {
lastCallTime = now;
}
// 2. 计算等待时间
const timeSinceLastCall = now - (lastCallTime || 0);
const shouldCallLeading = leading && timeSinceLastCall >= wait;
// 3. 最大等待时间处理(类似节流)
const maxWaitExpired =
maxWait !== undefined && now - (lastCallTime || 0) >= maxWait;
// 4. 执行策略
if (shouldCallLeading || maxWaitExpired) {
// 立即执行并重置状态
clearTimer();
lastCallTime = now;
lastResult = new OcPromise<Returns>((resolve) => {
pendingResolve = resolve;
invokeFunction();
});
} else if (trailing) {
// 延迟执行
lastResult = new OcPromise<Returns>((resolve) => {
pendingResolve = resolve;
startTimer(() => {
lastCallTime = Date.now();
invokeFunction();
}, wait);
});
} else {
// 不需要执行时返回 pending promise
lastResult = new OcPromise<Returns>(() => {});
}
lastResult.canceled(() => {
clearTimer();
lastCallTime = null;
lastArgs = null;
pendingResolve = null;
});
return lastResult;
};
return debounced;
}
export { debounce };