vite-uni-dev-tool
Version:
vite-uni-dev-tool, debug, uni-app, 一处编写,到处调试
268 lines (228 loc) • 7.35 kB
text/typescript
type DebounceOptions = {
leading?: boolean;
trailing?: boolean;
maxWait?: number; // 可选:最大等待时间
};
type DebouncedFunction<T extends (...args: any[]) => any> = ((
...args: Parameters<T>
) => ReturnType<T> | undefined) & {
cancel: () => void;
flush: () => ReturnType<T> | undefined;
};
/**
* 创建一个防抖函数,该函数在最后一次调用后等待指定时间才执行。
* @param func 需要防抖的函数
* @param wait 等待时间(毫秒)
* @param options 配置选项
* @param options.leading 是否在开始时立即执行
* @param options.trailing 是否在延迟结束后执行最后一次调用
* @returns 防抖后的函数
*/
export function debounce<T extends (...args: any[]) => any>(
func: T,
wait: number,
options: DebounceOptions = {},
): DebouncedFunction<T> {
const { leading = false, trailing = true } = options;
let timeout: ReturnType<typeof setTimeout> | null = null;
let lastArgs: Parameters<T> | null = null;
let lastThis: ThisParameterType<T> | null = null;
let result: ReturnType<T> | undefined;
let lastCallTime: number | null = null;
let lastInvokeTime: number = 0;
const invokeFunc = (time: number) => {
const args = lastArgs;
const thisArg = lastThis;
lastArgs = lastThis = null;
lastInvokeTime = time;
result = func.apply(thisArg, args!);
return result;
};
const leadingEdge = (time: number) => {
// 开始新的计时周期
lastInvokeTime = time;
// 如果 trailing 为 true,启动定时器执行最后一次调用
if (trailing) {
timeout = setTimeout(timerExpired, wait);
}
// 如果 leading 为 true,立即执行
return leading ? invokeFunc(time) : undefined;
};
const trailingEdge = (time: number) => {
timeout = null;
// 只有在 trailing 为 true 且有等待的调用时才执行
if (trailing && lastArgs) {
return invokeFunc(time);
}
lastArgs = lastThis = null;
return result;
};
const remainingWait = (time: number) => {
const timeSinceLastCall = time - (lastCallTime || 0);
const timeSinceLastInvoke = time - lastInvokeTime;
const timeWaiting = wait - timeSinceLastCall;
// 如果设置了最大等待时间,取最小值
return Math.min(timeWaiting, wait - timeSinceLastInvoke);
};
const shouldInvoke = (time: number) => {
const timeSinceLastCall = time - (lastCallTime || 0);
const timeSinceLastInvoke = time - lastInvokeTime;
// 第一次调用
if (lastCallTime === null) {
return leading && timeSinceLastInvoke < wait;
}
// 时间间隔超过等待时间,应该执行
if (timeSinceLastCall >= wait) {
return true;
}
// 如果设置了最大等待时间,并且超过最大等待时间
if (timeSinceLastInvoke >= wait) {
return true;
}
return false;
};
const timerExpired = () => {
const time = Date.now();
if (shouldInvoke(time)) {
trailingEdge(time);
} else if (timeout) {
// 继续等待
timeout = setTimeout(timerExpired, remainingWait(time));
}
};
const debounced = function (
this: ThisParameterType<T>,
...args: Parameters<T>
): ReturnType<T> | undefined {
const time = Date.now();
const isInvoking = shouldInvoke(time);
lastArgs = args;
lastThis = this;
lastCallTime = time;
if (isInvoking) {
if (timeout === null) {
// 第一次调用,且 leading 为 true
return leadingEdge(time);
}
// 如果不是第一次调用,但在等待时间后应该执行
else if (timeout) {
clearTimeout(timeout);
timeout = setTimeout(timerExpired, wait);
// 如果 leading 为 true,立即执行
if (leading) {
return invokeFunc(time);
}
}
} else {
// 没有触发执行条件,设置定时器
if (!timeout) {
timeout = setTimeout(timerExpired, wait);
}
}
return result;
};
debounced.cancel = (): void => {
if (timeout !== null) {
clearTimeout(timeout);
timeout = null;
lastArgs = lastThis = null;
lastCallTime = lastInvokeTime = 0;
}
};
debounced.flush = (): ReturnType<T> | undefined => {
if (timeout !== null) {
const time = Date.now();
const flushedResult = trailingEdge(time);
return flushedResult;
}
return result;
};
return debounced;
}
type ThrottleOptions = {
leading?: boolean;
trailing?: boolean;
};
/**
* 创建一个节流函数,在指定时间间隔内只执行一次原函数
* @param func 需要节流的函数
* @param wait 等待时间(毫秒)
* @param options 配置选项
* @param options.leading 是否在开始时立即执行
* @param options.trailing 是否在延迟结束后执行最后一次调用
* @returns 节流后的函数
*/
export function throttle<T extends (...args: any[]) => any>(
func: T,
wait: number,
options: ThrottleOptions = {},
): (...args: Parameters<T>) => ReturnType<T> | undefined {
let timeout: ReturnType<typeof setTimeout> | null = null;
let lastArgs: Parameters<T> | null = null;
let lastThis: any = null;
let result: ReturnType<T> | undefined;
let lastCallTime: number | null = null;
const { leading = true, trailing = true } = options;
const invokeFunc = (time: number) => {
const args = lastArgs;
const thisArg = lastThis;
lastArgs = lastThis = null;
lastCallTime = time;
result = func.apply(thisArg, args!);
return result;
};
const leadingEdge = (time: number) => {
lastCallTime = time;
timeout = null;
return leading ? invokeFunc(time) : undefined;
};
const trailingEdge = (time: number) => {
timeout = null;
if (lastArgs && trailing) {
return invokeFunc(time);
}
lastArgs = lastThis = null;
return result;
};
const remainingWait = (time: number) => {
if (lastCallTime === null) return wait;
const timeSinceLastCall = time - lastCallTime;
return wait - timeSinceLastCall;
};
const shouldInvoke = (time: number) => {
if (lastCallTime === null) return true;
const timeSinceLastCall = time - lastCallTime;
return timeSinceLastCall >= wait || timeSinceLastCall < 0;
};
const timerExpired = () => {
const time = Date.now();
if (shouldInvoke(time)) {
return trailingEdge(time);
}
timeout = setTimeout(timerExpired, remainingWait(time));
return undefined;
};
const throttled = function (this: any, ...args: Parameters<T>) {
const time = Date.now();
const isInvoking = shouldInvoke(time);
lastArgs = args;
lastThis = this;
if (isInvoking) {
if (timeout === null) {
return leadingEdge(time);
}
}
if (timeout === null) {
timeout = setTimeout(timerExpired, wait);
}
return result;
};
throttled.cancel = () => {
if (timeout !== null) {
clearTimeout(timeout);
}
lastCallTime = null;
timeout = lastArgs = lastThis = null;
};
return throttled;
}