@lytjs/common-timing 6.4.0 → 6.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +423 -415
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\r\n * @lytjs/common-timing\r\n * 定时与调度工具函数\r\n */\r\n\r\n/**\r\n * 防抖函数返回类型\r\n */\r\nexport interface DebouncedFn<T extends (...args: unknown[]) => unknown> {\r\n (...args: Parameters<T>): void;\r\n cancel: () => void;\r\n}\r\n\r\n/**\r\n * 防抖 - 延迟执行,在最后一次调用后等待指定时间\r\n */\r\nexport function debounce<T extends (...args: unknown[]) => unknown>(\r\n fn: T,\r\n waitMs: number,\r\n): DebouncedFn<T> {\r\n let timer: ReturnType<typeof setTimeout> | null = null;\r\n\r\n const debounced = ((...args: Parameters<T>) => {\r\n if (timer) clearTimeout(timer);\r\n timer = setTimeout(() => {\r\n fn(...args);\r\n timer = null;\r\n }, waitMs);\r\n }) as DebouncedFn<T>;\r\n\r\n debounced.cancel = () => {\r\n if (timer) {\r\n clearTimeout(timer);\r\n timer = null;\r\n }\r\n };\r\n\r\n return debounced;\r\n}\r\n\r\n/**\r\n * 立即防抖 - 立即执行一次,然后在等待期间不再执行\r\n */\r\nexport function debounceImmediate<T extends (...args: unknown[]) => unknown>(\r\n fn: T,\r\n waitMs: number,\r\n): DebouncedFn<T> {\r\n let timer: ReturnType<typeof setTimeout> | null = null;\r\n let isWaiting = false;\r\n\r\n const debounced = ((...args: Parameters<T>) => {\r\n if (!isWaiting) {\r\n fn(...args);\r\n isWaiting = true;\r\n timer = setTimeout(() => {\r\n isWaiting = false;\r\n timer = null;\r\n }, waitMs);\r\n }\r\n }) as DebouncedFn<T>;\r\n\r\n debounced.cancel = () => {\r\n if (timer) {\r\n clearTimeout(timer);\r\n timer = null;\r\n }\r\n isWaiting = false;\r\n };\r\n\r\n return debounced;\r\n}\r\n\r\n/**\r\n * 节流函数返回类型\r\n */\r\nexport interface ThrottledFn<T extends (...args: unknown[]) => unknown> {\r\n (...args: Parameters<T>): void;\r\n cancel: () => void;\r\n}\r\n\r\n/**\r\n * 节流 - 在指定时间内最多执行一次\r\n *\r\n * @note 由于浏览器 setTimeout 的最小延迟通常为 4ms(嵌套调用时),\r\n * 且在后台标签页中可能被进一步节流至 1000ms,实际节流间隔\r\n * 可能略大于指定的 waitMs。对于高精度定时需求,请考虑使用\r\n * requestAnimationFrame 或 Web Worker。\r\n */\r\nexport function throttle<T extends (...args: unknown[]) => unknown>(\r\n fn: T,\r\n waitMs: number,\r\n): ThrottledFn<T> {\r\n let timer: ReturnType<typeof setTimeout> | null = null;\r\n let lastCallTime = 0;\r\n let cancelled = false;\r\n\r\n const throttled = ((...args: Parameters<T>) => {\r\n if (cancelled) return;\r\n\r\n const now = Date.now();\r\n const elapsed = now - lastCallTime;\r\n\r\n if (elapsed >= waitMs) {\r\n lastCallTime = now;\r\n fn(...args);\r\n } else if (!timer) {\r\n timer = setTimeout(() => {\r\n lastCallTime = Date.now();\r\n fn(...args);\r\n timer = null;\r\n }, waitMs - elapsed);\r\n }\r\n }) as ThrottledFn<T>;\r\n\r\n throttled.cancel = () => {\r\n cancelled = true;\r\n if (timer) {\r\n clearTimeout(timer);\r\n timer = null;\r\n }\r\n };\r\n\r\n return throttled;\r\n}\r\n\r\n/**\r\n * 带尾调的节流 - 立即执行 + 等待结束后再执行最后一次\r\n */\r\nexport function throttleWithTrailing<T extends (...args: unknown[]) => unknown>(\r\n fn: T,\r\n waitMs: number,\r\n): ThrottledFn<T> {\r\n let timer: ReturnType<typeof setTimeout> | null = null;\r\n let lastArgs: Parameters<T> | null = null;\r\n let isThrottled = false;\r\n\r\n const throttled = ((...args: Parameters<T>) => {\r\n lastArgs = args;\r\n if (!isThrottled) {\r\n isThrottled = true;\r\n fn(...args);\r\n timer = setTimeout(() => {\r\n isThrottled = false;\r\n if (lastArgs) {\r\n fn(...lastArgs);\r\n lastArgs = null;\r\n }\r\n timer = null;\r\n }, waitMs);\r\n }\r\n }) as ThrottledFn<T>;\r\n\r\n throttled.cancel = () => {\r\n if (timer) {\r\n clearTimeout(timer);\r\n timer = null;\r\n }\r\n isThrottled = false;\r\n lastArgs = null;\r\n };\r\n\r\n return throttled;\r\n}\r\n\r\n/**\r\n * 延迟指定时间\r\n */\r\nexport function delay<T = void>(ms: number, value?: T): Promise<T> {\r\n return new Promise((resolve) => {\r\n setTimeout(() => resolve(value as T), ms);\r\n });\r\n}\r\n\r\n/**\r\n * 重试函数\r\n */\r\nexport async function retry<T>(\r\n fn: () => Promise<T>,\r\n maxRetries: number = 3,\r\n retryDelay: number = 1000,\r\n retryCondition?: (error: Error) => boolean,\r\n): Promise<T> {\r\n // maxRetries=0 表示不重试,直接执行一次\r\n if (maxRetries <= 0) {\r\n return fn();\r\n }\r\n\r\n let lastError: Error | undefined;\r\n\r\n for (let attempt = 0; attempt < maxRetries; attempt++) {\r\n try {\r\n return await fn();\r\n } catch (err) {\r\n lastError = err instanceof Error ? err : new Error(String(err));\r\n\r\n if (attempt < maxRetries - 1) {\r\n if (retryCondition && !retryCondition(lastError)) {\r\n throw lastError;\r\n }\r\n await delay(retryDelay);\r\n }\r\n }\r\n }\r\n\r\n throw lastError!;\r\n}\r\n\r\n/**\r\n * 超时包装\r\n */\r\nexport function timeout<T>(\r\n promise: Promise<T>,\r\n ms: number,\r\n message: string = 'Timeout',\r\n): Promise<T> {\r\n return new Promise((resolve, reject) => {\r\n const timer = setTimeout(() => {\r\n reject(new Error(message));\r\n }, ms);\r\n\r\n promise.then(\r\n (value) => {\r\n clearTimeout(timer);\r\n resolve(value);\r\n },\r\n (err) => {\r\n clearTimeout(timer);\r\n reject(err);\r\n },\r\n );\r\n });\r\n}\r\n\r\n/**\r\n * 轮询直到条件满足\r\n */\r\nexport async function poll<T>(\r\n fn: () => T | Promise<T>,\r\n condition: (value: T) => boolean,\r\n interval: number = 1000,\r\n timeoutMs: number = 30000,\r\n): Promise<T> {\r\n const startTime = Date.now();\r\n let consecutiveErrors = 0;\r\n const MAX_CONSECUTIVE_ERRORS = 10;\r\n\r\n while (true) {\r\n try {\r\n const value = await fn();\r\n consecutiveErrors = 0;\r\n if (condition(value)) {\r\n return value;\r\n }\r\n } catch (err) {\r\n consecutiveErrors++;\r\n if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {\r\n throw new Error(\r\n `Polling aborted after ${MAX_CONSECUTIVE_ERRORS} consecutive errors: ${err instanceof Error ? err.message : String(err)}`,\r\n );\r\n }\r\n }\r\n\r\n if (Date.now() - startTime >= timeoutMs) {\r\n throw new Error('Polling timeout exceeded');\r\n }\r\n\r\n await delay(interval);\r\n }\r\n}\r\n\r\n/**\r\n * 任务队列 - 控制并发执行的任务队列\r\n */\r\nexport class TaskQueue {\r\n private queue: Array<() => Promise<void>> = [];\r\n private running: number = 0;\r\n private concurrency: number;\r\n private resolvers: Array<() => void> = [];\r\n\r\n constructor(concurrency: number = 1) {\r\n if (concurrency < 1) {\r\n throw new Error('TaskQueue concurrency must be at least 1');\r\n }\r\n this.concurrency = concurrency;\r\n }\r\n\r\n get size(): number {\r\n return this.queue.length;\r\n }\r\n\r\n add(task: () => Promise<void>): void {\r\n this.queue.push(task);\r\n this.run();\r\n }\r\n\r\n private async run(): Promise<void> {\r\n if (this.running >= this.concurrency || this.queue.length === 0) return;\r\n\r\n this.running++;\r\n const task = this.queue.shift()!;\r\n\r\n try {\r\n await task();\r\n } catch (err) {\r\n console.error('TaskQueue task error:', err);\r\n } finally {\r\n this.running--;\r\n if (this.queue.length > 0) {\r\n this.run();\r\n } else if (this.running === 0) {\r\n this.resolvers.forEach((resolve) => resolve());\r\n this.resolvers = [];\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * 等待所有任务完成\r\n */\r\n async wait(): Promise<void> {\r\n if (this.running === 0 && this.queue.length === 0) return;\r\n return new Promise<void>((resolve) => {\r\n this.resolvers.push(resolve);\r\n });\r\n }\r\n\r\n /**\r\n * 清空待执行的任务队列\r\n */\r\n clear(): void {\r\n this.queue = [];\r\n }\r\n}\r\n\r\n// ============================================================\r\n// 函数工具(从 @lytjs/shared 迁移)\r\n// ============================================================\r\n\r\n/**\r\n * 返回自身的恒等函数\r\n *\r\n * @param value - 输入值\r\n * @returns 输入值本身\r\n * @example\r\n * ```ts\r\n * identity(42) // 42\r\n * identity({ a: 1 }) // { a: 1 }\r\n * ```\r\n */\r\nexport function identity<T>(value: T): T {\r\n return value;\r\n}\r\n\r\n/**\r\n * 创建一个始终返回指定值的函数\r\n *\r\n * @param value - 要返回的值\r\n * @returns 返回该值的函数\r\n * @example\r\n * ```ts\r\n * const alwaysTrue = constant(true)\r\n * alwaysTrue() // true\r\n * ```\r\n */\r\nexport function constant<T>(value: T): () => T {\r\n return () => value;\r\n}\r\n\r\n/**\r\n * 只执行一次的函数\r\n *\r\n * FIX: P2-v11-27 改进 once 函数:支持异步函数场景,\r\n * 在异步执行期间阻止重复调用,执行完成后正确返回结果\r\n *\r\n * @param fn - 要执行的函数\r\n * @returns 只执行一次的包装函数\r\n * @example\r\n * ```ts\r\n * const init = once(() => console.log('initialized'))\r\n * init() // 输出 'initialized'\r\n * init() // 无输出\r\n * ```\r\n */\r\nexport function once<T extends (...args: unknown[]) => unknown>(\r\n fn: T,\r\n): (...args: Parameters<T>) => ReturnType<T> | undefined | Promise<ReturnType<T> | undefined> {\r\n let called = false;\r\n let result: ReturnType<T>;\r\n let pendingPromise: Promise<ReturnType<T>> | null = null;\r\n\r\n return (...args: Parameters<T>): ReturnType<T> | undefined | Promise<ReturnType<T> | undefined> => {\r\n if (!called) {\r\n called = true;\r\n try {\r\n result = fn(...args) as ReturnType<T>;\r\n // 如果结果是 Promise,跟踪其状态以防止并发调用\r\n if (result instanceof Promise) {\r\n pendingPromise = result;\r\n return result.then(\r\n (val) => { pendingPromise = null; return val; },\r\n (err) => { pendingPromise = null; throw err; },\r\n ) as Promise<ReturnType<T> | undefined>;\r\n }\r\n return result;\r\n } catch (e) {\r\n // 同步异常:重置 called 标志,允许重试\r\n called = false;\r\n throw e;\r\n }\r\n }\r\n // 如果有正在执行的异步操作,返回其 Promise\r\n if (pendingPromise) return pendingPromise;\r\n return undefined;\r\n };\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @lytjs/common-timing\n * 定时与调度工具函数\n */\n\n/**\n * 防抖函数返回类型\n */\nexport interface DebouncedFn<T extends (...args: unknown[]) => unknown> {\n (...args: Parameters<T>): void;\n cancel: () => void;\n}\n\n/**\n * 防抖 - 延迟执行,在最后一次调用后等待指定时间\n */\nexport function debounce<T extends (...args: unknown[]) => unknown>(\n fn: T,\n waitMs: number,\n): DebouncedFn<T> {\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n const debounced = ((...args: Parameters<T>) => {\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => {\n fn(...args);\n timer = null;\n }, waitMs);\n }) as DebouncedFn<T>;\n\n debounced.cancel = () => {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n };\n\n return debounced;\n}\n\n/**\n * 立即防抖 - 立即执行一次,然后在等待期间不再执行\n */\nexport function debounceImmediate<T extends (...args: unknown[]) => unknown>(\n fn: T,\n waitMs: number,\n): DebouncedFn<T> {\n let timer: ReturnType<typeof setTimeout> | null = null;\n let isWaiting = false;\n\n const debounced = ((...args: Parameters<T>) => {\n if (!isWaiting) {\n fn(...args);\n isWaiting = true;\n timer = setTimeout(() => {\n isWaiting = false;\n timer = null;\n }, waitMs);\n }\n }) as DebouncedFn<T>;\n\n debounced.cancel = () => {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n isWaiting = false;\n };\n\n return debounced;\n}\n\n/**\n * 节流函数返回类型\n */\nexport interface ThrottledFn<T extends (...args: unknown[]) => unknown> {\n (...args: Parameters<T>): void;\n cancel: () => void;\n}\n\n/**\n * 节流 - 在指定时间内最多执行一次\n *\n * @note 由于浏览器 setTimeout 的最小延迟通常为 4ms(嵌套调用时),\n * 且在后台标签页中可能被进一步节流至 1000ms,实际节流间隔\n * 可能略大于指定的 waitMs。对于高精度定时需求,请考虑使用\n * requestAnimationFrame 或 Web Worker。\n */\nexport function throttle<T extends (...args: unknown[]) => unknown>(\n fn: T,\n waitMs: number,\n): ThrottledFn<T> {\n let timer: ReturnType<typeof setTimeout> | null = null;\n let lastCallTime = 0;\n let cancelled = false;\n\n const throttled = ((...args: Parameters<T>) => {\n if (cancelled) return;\n\n const now = Date.now();\n const elapsed = now - lastCallTime;\n\n if (elapsed >= waitMs) {\n lastCallTime = now;\n fn(...args);\n } else if (!timer) {\n timer = setTimeout(() => {\n lastCallTime = Date.now();\n fn(...args);\n timer = null;\n }, waitMs - elapsed);\n }\n }) as ThrottledFn<T>;\n\n throttled.cancel = () => {\n cancelled = true;\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n };\n\n return throttled;\n}\n\n/**\n * 带尾调的节流 - 立即执行 + 等待结束后再执行最后一次\n */\nexport function throttleWithTrailing<T extends (...args: unknown[]) => unknown>(\n fn: T,\n waitMs: number,\n): ThrottledFn<T> {\n let timer: ReturnType<typeof setTimeout> | null = null;\n let lastArgs: Parameters<T> | null = null;\n let isThrottled = false;\n\n const throttled = ((...args: Parameters<T>) => {\n lastArgs = args;\n if (!isThrottled) {\n isThrottled = true;\n fn(...args);\n timer = setTimeout(() => {\n isThrottled = false;\n if (lastArgs) {\n fn(...lastArgs);\n lastArgs = null;\n }\n timer = null;\n }, waitMs);\n }\n }) as ThrottledFn<T>;\n\n throttled.cancel = () => {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n isThrottled = false;\n lastArgs = null;\n };\n\n return throttled;\n}\n\n/**\n * 延迟指定时间\n */\nexport function delay<T = void>(ms: number, value?: T): Promise<T> {\n return new Promise((resolve) => {\n setTimeout(() => resolve(value as T), ms);\n });\n}\n\n/**\n * 重试函数\n */\nexport async function retry<T>(\n fn: () => Promise<T>,\n maxRetries: number = 3,\n retryDelay: number = 1000,\n retryCondition?: (error: Error) => boolean,\n): Promise<T> {\n // maxRetries=0 表示不重试,直接执行一次\n if (maxRetries <= 0) {\n return fn();\n }\n\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n return await fn();\n } catch (err) {\n lastError = err instanceof Error ? err : new Error(String(err));\n\n if (attempt < maxRetries - 1) {\n if (retryCondition && !retryCondition(lastError)) {\n throw lastError;\n }\n await delay(retryDelay);\n }\n }\n }\n\n throw lastError!;\n}\n\n/**\n * 超时包装\n */\nexport function timeout<T>(\n promise: Promise<T>,\n ms: number,\n message: string = 'Timeout',\n): Promise<T> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n reject(new Error(message));\n }, ms);\n\n promise.then(\n (value) => {\n clearTimeout(timer);\n resolve(value);\n },\n (err) => {\n clearTimeout(timer);\n reject(err);\n },\n );\n });\n}\n\n/**\n * 轮询直到条件满足\n */\nexport async function poll<T>(\n fn: () => T | Promise<T>,\n condition: (value: T) => boolean,\n interval: number = 1000,\n timeoutMs: number = 30000,\n): Promise<T> {\n const startTime = Date.now();\n let consecutiveErrors = 0;\n const MAX_CONSECUTIVE_ERRORS = 10;\n\n while (true) {\n try {\n const value = await fn();\n consecutiveErrors = 0;\n if (condition(value)) {\n return value;\n }\n } catch (err) {\n consecutiveErrors++;\n if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {\n throw new Error(\n `Polling aborted after ${MAX_CONSECUTIVE_ERRORS} consecutive errors: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n if (Date.now() - startTime >= timeoutMs) {\n throw new Error('Polling timeout exceeded');\n }\n\n await delay(interval);\n }\n}\n\n/**\n * 任务队列 - 控制并发执行的任务队列\n */\nexport class TaskQueue {\n private queue: Array<() => Promise<void>> = [];\n private running: number = 0;\n private concurrency: number;\n private resolvers: Array<() => void> = [];\n\n constructor(concurrency: number = 1) {\n if (concurrency < 1) {\n throw new Error('TaskQueue concurrency must be at least 1');\n }\n this.concurrency = concurrency;\n }\n\n get size(): number {\n return this.queue.length;\n }\n\n add(task: () => Promise<void>): void {\n this.queue.push(task);\n this.run();\n }\n\n private async run(): Promise<void> {\n if (this.running >= this.concurrency || this.queue.length === 0) return;\n\n this.running++;\n const task = this.queue.shift()!;\n\n try {\n await task();\n } catch (err) {\n console.error('TaskQueue task error:', err);\n } finally {\n this.running--;\n if (this.queue.length > 0) {\n this.run();\n } else if (this.running === 0) {\n this.resolvers.forEach((resolve) => resolve());\n this.resolvers = [];\n }\n }\n }\n\n /**\n * 等待所有任务完成\n */\n async wait(): Promise<void> {\n if (this.running === 0 && this.queue.length === 0) return;\n return new Promise<void>((resolve) => {\n this.resolvers.push(resolve);\n });\n }\n\n /**\n * 清空待执行的任务队列\n */\n clear(): void {\n this.queue = [];\n }\n}\n\n// ============================================================\n// 函数工具(从 @lytjs/shared 迁移)\n// ============================================================\n\n/**\n * 返回自身的恒等函数\n *\n * @param value - 输入值\n * @returns 输入值本身\n * @example\n * ```ts\n * identity(42) // 42\n * identity({ a: 1 }) // { a: 1 }\n * ```\n */\nexport function identity<T>(value: T): T {\n return value;\n}\n\n/**\n * 创建一个始终返回指定值的函数\n *\n * @param value - 要返回的值\n * @returns 返回该值的函数\n * @example\n * ```ts\n * const alwaysTrue = constant(true)\n * alwaysTrue() // true\n * ```\n */\nexport function constant<T>(value: T): () => T {\n return () => value;\n}\n\n/**\n * 只执行一次的函数\n *\n * FIX: P2-v11-27 改进 once 函数:支持异步函数场景,\n * 在异步执行期间阻止重复调用,执行完成后正确返回结果\n *\n * @param fn - 要执行的函数\n * @returns 只执行一次的包装函数\n * @example\n * ```ts\n * const init = once(() => console.log('initialized'))\n * init() // 输出 'initialized'\n * init() // 无输出\n * ```\n */\nexport function once<T extends (...args: unknown[]) => unknown>(\n fn: T,\n): (...args: Parameters<T>) => ReturnType<T> | undefined | Promise<ReturnType<T> | undefined> {\n let called = false;\n let result: ReturnType<T>;\n let pendingPromise: Promise<ReturnType<T>> | null = null;\n\n return (\n ...args: Parameters<T>\n ): ReturnType<T> | undefined | Promise<ReturnType<T> | undefined> => {\n if (!called) {\n called = true;\n try {\n result = fn(...args) as ReturnType<T>;\n // 如果结果是 Promise,跟踪其状态以防止并发调用\n if (result instanceof Promise) {\n pendingPromise = result;\n return result.then(\n (val) => {\n pendingPromise = null;\n return val;\n },\n (err) => {\n pendingPromise = null;\n throw err;\n },\n ) as Promise<ReturnType<T> | undefined>;\n }\n return result;\n } catch (e) {\n // 同步异常:重置 called 标志,允许重试\n called = false;\n throw e;\n }\n }\n // 如果有正在执行的异步操作,返回其 Promise\n if (pendingPromise) return pendingPromise;\n return undefined;\n };\n}\n"]}
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";AAgBO,SAAS,QAAA,CACd,IACA,MAAA,EACgB;AAChB,EAAA,IAAI,KAAA,GAA8C,IAAA;AAElD,EAAA,MAAM,SAAA,IAAa,IAAI,IAAA,KAAwB;AAC7C,IAAA,IAAI,KAAA,eAAoB,KAAK,CAAA;AAC7B,IAAA,KAAA,GAAQ,WAAW,MAAM;AACvB,MAAA,EAAA,CAAG,GAAG,IAAI,CAAA;AACV,MAAA,KAAA,GAAQ,IAAA;AAAA,IACV,GAAG,MAAM,CAAA;AAAA,EACX,CAAA,CAAA;AAEA,EAAA,SAAA,CAAU,SAAS,MAAM;AACvB,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,YAAA,CAAa,KAAK,CAAA;AAClB,MAAA,KAAA,GAAQ,IAAA;AAAA,IACV;AAAA,EACF,CAAA;AAEA,EAAA,OAAO,SAAA;AACT;AAKO,SAAS,iBAAA,CACd,IACA,MAAA,EACgB;AAChB,EAAA,IAAI,KAAA,GAA8C,IAAA;AAClD,EAAA,IAAI,SAAA,GAAY,KAAA;AAEhB,EAAA,MAAM,SAAA,IAAa,IAAI,IAAA,KAAwB;AAC7C,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA,EAAA,CAAG,GAAG,IAAI,CAAA;AACV,MAAA,SAAA,GAAY,IAAA;AACZ,MAAA,KAAA,GAAQ,WAAW,MAAM;AACvB,QAAA,SAAA,GAAY,KAAA;AACZ,QAAA,KAAA,GAAQ,IAAA;AAAA,MACV,GAAG,MAAM,CAAA;AAAA,IACX;AAAA,EACF,CAAA,CAAA;AAEA,EAAA,SAAA,CAAU,SAAS,MAAM;AACvB,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,YAAA,CAAa,KAAK,CAAA;AAClB,MAAA,KAAA,GAAQ,IAAA;AAAA,IACV;AACA,IAAA,SAAA,GAAY,KAAA;AAAA,EACd,CAAA;AAEA,EAAA,OAAO,SAAA;AACT;AAkBO,SAAS,QAAA,CACd,IACA,MAAA,EACgB;AAChB,EAAA,IAAI,KAAA,GAA8C,IAAA;AAClD,EAAA,IAAI,YAAA,GAAe,CAAA;AACnB,EAAA,IAAI,SAAA,GAAY,KAAA;AAEhB,EAAA,MAAM,SAAA,IAAa,IAAI,IAAA,KAAwB;AAC7C,IAAA,IAAI,SAAA,EAAW;AAEf,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,IAAA,MAAM,UAAU,GAAA,GAAM,YAAA;AAEtB,IAAA,IAAI,WAAW,MAAA,EAAQ;AACrB,MAAA,YAAA,GAAe,GAAA;AACf,MAAA,EAAA,CAAG,GAAG,IAAI,CAAA;AAAA,IACZ,CAAA,MAAA,IAAW,CAAC,KAAA,EAAO;AACjB,MAAA,KAAA,GAAQ,WAAW,MAAM;AACvB,QAAA,YAAA,GAAe,KAAK,GAAA,EAAI;AACxB,QAAA,EAAA,CAAG,GAAG,IAAI,CAAA;AACV,QAAA,KAAA,GAAQ,IAAA;AAAA,MACV,CAAA,EAAG,SAAS,OAAO,CAAA;AAAA,IACrB;AAAA,EACF,CAAA,CAAA;AAEA,EAAA,SAAA,CAAU,SAAS,MAAM;AACvB,IAAA,SAAA,GAAY,IAAA;AACZ,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,YAAA,CAAa,KAAK,CAAA;AAClB,MAAA,KAAA,GAAQ,IAAA;AAAA,IACV;AAAA,EACF,CAAA;AAEA,EAAA,OAAO,SAAA;AACT;AAKO,SAAS,oBAAA,CACd,IACA,MAAA,EACgB;AAChB,EAAA,IAAI,KAAA,GAA8C,IAAA;AAClD,EAAA,IAAI,QAAA,GAAiC,IAAA;AACrC,EAAA,IAAI,WAAA,GAAc,KAAA;AAElB,EAAA,MAAM,SAAA,IAAa,IAAI,IAAA,KAAwB;AAC7C,IAAA,QAAA,GAAW,IAAA;AACX,IAAA,IAAI,CAAC,WAAA,EAAa;AAChB,MAAA,WAAA,GAAc,IAAA;AACd,MAAA,EAAA,CAAG,GAAG,IAAI,CAAA;AACV,MAAA,KAAA,GAAQ,WAAW,MAAM;AACvB,QAAA,WAAA,GAAc,KAAA;AACd,QAAA,IAAI,QAAA,EAAU;AACZ,UAAA,EAAA,CAAG,GAAG,QAAQ,CAAA;AACd,UAAA,QAAA,GAAW,IAAA;AAAA,QACb;AACA,QAAA,KAAA,GAAQ,IAAA;AAAA,MACV,GAAG,MAAM,CAAA;AAAA,IACX;AAAA,EACF,CAAA,CAAA;AAEA,EAAA,SAAA,CAAU,SAAS,MAAM;AACvB,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,YAAA,CAAa,KAAK,CAAA;AAClB,MAAA,KAAA,GAAQ,IAAA;AAAA,IACV;AACA,IAAA,WAAA,GAAc,KAAA;AACd,IAAA,QAAA,GAAW,IAAA;AAAA,EACb,CAAA;AAEA,EAAA,OAAO,SAAA;AACT;AAKO,SAAS,KAAA,CAAgB,IAAY,KAAA,EAAuB;AACjE,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,KAAY;AAC9B,IAAA,UAAA,CAAW,MAAM,OAAA,CAAQ,KAAU,CAAA,EAAG,EAAE,CAAA;AAAA,EAC1C,CAAC,CAAA;AACH;AAKA,eAAsB,MACpB,EAAA,EACA,UAAA,GAAqB,CAAA,EACrB,UAAA,GAAqB,KACrB,cAAA,EACY;AAEZ,EAAA,IAAI,cAAc,CAAA,EAAG;AACnB,IAAA,OAAO,EAAA,EAAG;AAAA,EACZ;AAEA,EAAA,IAAI,SAAA;AAEJ,EAAA,KAAA,IAAS,OAAA,GAAU,CAAA,EAAG,OAAA,GAAU,UAAA,EAAY,OAAA,EAAA,EAAW;AACrD,IAAA,IAAI;AACF,MAAA,OAAO,MAAM,EAAA,EAAG;AAAA,IAClB,SAAS,GAAA,EAAK;AACZ,MAAA,SAAA,GAAY,eAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,KAAA,CAAM,MAAA,CAAO,GAAG,CAAC,CAAA;AAE9D,MAAA,IAAI,OAAA,GAAU,aAAa,CAAA,EAAG;AAC5B,QAAA,IAAI,cAAA,IAAkB,CAAC,cAAA,CAAe,SAAS,CAAA,EAAG;AAChD,UAAA,MAAM,SAAA;AAAA,QACR;AACA,QAAA,MAAM,MAAM,UAAU,CAAA;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AAEA,EAAA,MAAM,SAAA;AACR;AAKO,SAAS,OAAA,CACd,OAAA,EACA,EAAA,EACA,OAAA,GAAkB,SAAA,EACN;AACZ,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AACtC,IAAA,MAAM,KAAA,GAAQ,WAAW,MAAM;AAC7B,MAAA,MAAA,CAAO,IAAI,KAAA,CAAM,OAAO,CAAC,CAAA;AAAA,IAC3B,GAAG,EAAE,CAAA;AAEL,IAAA,OAAA,CAAQ,IAAA;AAAA,MACN,CAAC,KAAA,KAAU;AACT,QAAA,YAAA,CAAa,KAAK,CAAA;AAClB,QAAA,OAAA,CAAQ,KAAK,CAAA;AAAA,MACf,CAAA;AAAA,MACA,CAAC,GAAA,KAAQ;AACP,QAAA,YAAA,CAAa,KAAK,CAAA;AAClB,QAAA,MAAA,CAAO,GAAG,CAAA;AAAA,MACZ;AAAA,KACF;AAAA,EACF,CAAC,CAAA;AACH;AAKA,eAAsB,KACpB,EAAA,EACA,SAAA,EACA,QAAA,GAAmB,GAAA,EACnB,YAAoB,GAAA,EACR;AACZ,EAAA,MAAM,SAAA,GAAY,KAAK,GAAA,EAAI;AAC3B,EAAA,IAAI,iBAAA,GAAoB,CAAA;AACxB,EAAA,MAAM,sBAAA,GAAyB,EAAA;AAE/B,EAAA,OAAO,IAAA,EAAM;AACX,IAAA,IAAI;AACF,MAAA,MAAM,KAAA,GAAQ,MAAM,EAAA,EAAG;AACvB,MAAA,iBAAA,GAAoB,CAAA;AACpB,MAAA,IAAI,SAAA,CAAU,KAAK,CAAA,EAAG;AACpB,QAAA,OAAO,KAAA;AAAA,MACT;AAAA,IACF,SAAS,GAAA,EAAK;AACZ,MAAA,iBAAA,EAAA;AACA,MAAA,IAAI,qBAAqB,sBAAA,EAAwB;AAC/C,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAA,sBAAA,EAAyB,sBAAsB,CAAA,qBAAA,EAAwB,GAAA,YAAe,QAAQ,GAAA,CAAI,OAAA,GAAU,MAAA,CAAO,GAAG,CAAC,CAAA;AAAA,SACzH;AAAA,MACF;AAAA,IACF;AAEA,IAAA,IAAI,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA,IAAa,SAAA,EAAW;AACvC,MAAA,MAAM,IAAI,MAAM,0BAA0B,CAAA;AAAA,IAC5C;AAEA,IAAA,MAAM,MAAM,QAAQ,CAAA;AAAA,EACtB;AACF;AAKO,IAAM,YAAN,MAAgB;AAAA,EAMrB,WAAA,CAAY,cAAsB,CAAA,EAAG;AALrC,IAAA,IAAA,CAAQ,QAAoC,EAAC;AAC7C,IAAA,IAAA,CAAQ,OAAA,GAAkB,CAAA;AAE1B,IAAA,IAAA,CAAQ,YAA+B,EAAC;AAGtC,IAAA,IAAI,cAAc,CAAA,EAAG;AACnB,MAAA,MAAM,IAAI,MAAM,0CAA0C,CAAA;AAAA,IAC5D;AACA,IAAA,IAAA,CAAK,WAAA,GAAc,WAAA;AAAA,EACrB;AAAA,EAEA,IAAI,IAAA,GAAe;AACjB,IAAA,OAAO,KAAK,KAAA,CAAM,MAAA;AAAA,EACpB;AAAA,EAEA,IAAI,IAAA,EAAiC;AACnC,IAAA,IAAA,CAAK,KAAA,CAAM,KAAK,IAAI,CAAA;AACpB,IAAA,IAAA,CAAK,GAAA,EAAI;AAAA,EACX;AAAA,EAEA,MAAc,GAAA,GAAqB;AACjC,IAAA,IAAI,KAAK,OAAA,IAAW,IAAA,CAAK,eAAe,IAAA,CAAK,KAAA,CAAM,WAAW,CAAA,EAAG;AAEjE,IAAA,IAAA,CAAK,OAAA,EAAA;AACL,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,KAAA,EAAM;AAE9B,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,EAAK;AAAA,IACb,SAAS,GAAA,EAAK;AACZ,MAAA,OAAA,CAAQ,KAAA,CAAM,yBAAyB,GAAG,CAAA;AAAA,IAC5C,CAAA,SAAE;AACA,MAAA,IAAA,CAAK,OAAA,EAAA;AACL,MAAA,IAAI,IAAA,CAAK,KAAA,CAAM,MAAA,GAAS,CAAA,EAAG;AACzB,QAAA,IAAA,CAAK,GAAA,EAAI;AAAA,MACX,CAAA,MAAA,IAAW,IAAA,CAAK,OAAA,KAAY,CAAA,EAAG;AAC7B,QAAA,IAAA,CAAK,SAAA,CAAU,OAAA,CAAQ,CAAC,OAAA,KAAY,SAAS,CAAA;AAC7C,QAAA,IAAA,CAAK,YAAY,EAAC;AAAA,MACpB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,IAAA,GAAsB;AAC1B,IAAA,IAAI,KAAK,OAAA,KAAY,CAAA,IAAK,IAAA,CAAK,KAAA,CAAM,WAAW,CAAA,EAAG;AACnD,IAAA,OAAO,IAAI,OAAA,CAAc,CAAC,OAAA,KAAY;AACpC,MAAA,IAAA,CAAK,SAAA,CAAU,KAAK,OAAO,CAAA;AAAA,IAC7B,CAAC,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,KAAA,GAAc;AACZ,IAAA,IAAA,CAAK,QAAQ,EAAC;AAAA,EAChB;AACF;AAiBO,SAAS,SAAY,KAAA,EAAa;AACvC,EAAA,OAAO,KAAA;AACT;AAaO,SAAS,SAAY,KAAA,EAAmB;AAC7C,EAAA,OAAO,MAAM,KAAA;AACf;AAiBO,SAAS,KACd,EAAA,EAC4F;AAC5F,EAAA,IAAI,MAAA,GAAS,KAAA;AACb,EAAA,IAAI,MAAA;AACJ,EAAA,IAAI,cAAA,GAAgD,IAAA;AAEpD,EAAA,OAAO,IAAI,IAAA,KAAwF;AACjG,IAAA,IAAI,CAAC,MAAA,EAAQ;AACX,MAAA,MAAA,GAAS,IAAA;AACT,MAAA,IAAI;AACF,QAAA,MAAA,GAAS,EAAA,CAAG,GAAG,IAAI,CAAA;AAEnB,QAAA,IAAI,kBAAkB,OAAA,EAAS;AAC7B,UAAA,cAAA,GAAiB,MAAA;AACjB,UAAA,OAAO,MAAA,CAAO,IAAA;AAAA,YACZ,CAAC,GAAA,KAAQ;AAAE,cAAA,cAAA,GAAiB,IAAA;AAAM,cAAA,OAAO,GAAA;AAAA,YAAK,CAAA;AAAA,YAC9C,CAAC,GAAA,KAAQ;AAAE,cAAA,cAAA,GAAiB,IAAA;AAAM,cAAA,MAAM,GAAA;AAAA,YAAK;AAAA,WAC/C;AAAA,QACF;AACA,QAAA,OAAO,MAAA;AAAA,MACT,SAAS,CAAA,EAAG;AAEV,QAAA,MAAA,GAAS,KAAA;AACT,QAAA,MAAM,CAAA;AAAA,MACR;AAAA,IACF;AAEA,IAAA,IAAI,gBAAgB,OAAO,cAAA;AAC3B,IAAA,OAAO,MAAA;AAAA,EACT,CAAA;AACF","file":"index.mjs","sourcesContent":["/**\r\n * @lytjs/common-timing\r\n * 定时与调度工具函数\r\n */\r\n\r\n/**\r\n * 防抖函数返回类型\r\n */\r\nexport interface DebouncedFn<T extends (...args: unknown[]) => unknown> {\r\n (...args: Parameters<T>): void;\r\n cancel: () => void;\r\n}\r\n\r\n/**\r\n * 防抖 - 延迟执行,在最后一次调用后等待指定时间\r\n */\r\nexport function debounce<T extends (...args: unknown[]) => unknown>(\r\n fn: T,\r\n waitMs: number,\r\n): DebouncedFn<T> {\r\n let timer: ReturnType<typeof setTimeout> | null = null;\r\n\r\n const debounced = ((...args: Parameters<T>) => {\r\n if (timer) clearTimeout(timer);\r\n timer = setTimeout(() => {\r\n fn(...args);\r\n timer = null;\r\n }, waitMs);\r\n }) as DebouncedFn<T>;\r\n\r\n debounced.cancel = () => {\r\n if (timer) {\r\n clearTimeout(timer);\r\n timer = null;\r\n }\r\n };\r\n\r\n return debounced;\r\n}\r\n\r\n/**\r\n * 立即防抖 - 立即执行一次,然后在等待期间不再执行\r\n */\r\nexport function debounceImmediate<T extends (...args: unknown[]) => unknown>(\r\n fn: T,\r\n waitMs: number,\r\n): DebouncedFn<T> {\r\n let timer: ReturnType<typeof setTimeout> | null = null;\r\n let isWaiting = false;\r\n\r\n const debounced = ((...args: Parameters<T>) => {\r\n if (!isWaiting) {\r\n fn(...args);\r\n isWaiting = true;\r\n timer = setTimeout(() => {\r\n isWaiting = false;\r\n timer = null;\r\n }, waitMs);\r\n }\r\n }) as DebouncedFn<T>;\r\n\r\n debounced.cancel = () => {\r\n if (timer) {\r\n clearTimeout(timer);\r\n timer = null;\r\n }\r\n isWaiting = false;\r\n };\r\n\r\n return debounced;\r\n}\r\n\r\n/**\r\n * 节流函数返回类型\r\n */\r\nexport interface ThrottledFn<T extends (...args: unknown[]) => unknown> {\r\n (...args: Parameters<T>): void;\r\n cancel: () => void;\r\n}\r\n\r\n/**\r\n * 节流 - 在指定时间内最多执行一次\r\n *\r\n * @note 由于浏览器 setTimeout 的最小延迟通常为 4ms(嵌套调用时),\r\n * 且在后台标签页中可能被进一步节流至 1000ms,实际节流间隔\r\n * 可能略大于指定的 waitMs。对于高精度定时需求,请考虑使用\r\n * requestAnimationFrame 或 Web Worker。\r\n */\r\nexport function throttle<T extends (...args: unknown[]) => unknown>(\r\n fn: T,\r\n waitMs: number,\r\n): ThrottledFn<T> {\r\n let timer: ReturnType<typeof setTimeout> | null = null;\r\n let lastCallTime = 0;\r\n let cancelled = false;\r\n\r\n const throttled = ((...args: Parameters<T>) => {\r\n if (cancelled) return;\r\n\r\n const now = Date.now();\r\n const elapsed = now - lastCallTime;\r\n\r\n if (elapsed >= waitMs) {\r\n lastCallTime = now;\r\n fn(...args);\r\n } else if (!timer) {\r\n timer = setTimeout(() => {\r\n lastCallTime = Date.now();\r\n fn(...args);\r\n timer = null;\r\n }, waitMs - elapsed);\r\n }\r\n }) as ThrottledFn<T>;\r\n\r\n throttled.cancel = () => {\r\n cancelled = true;\r\n if (timer) {\r\n clearTimeout(timer);\r\n timer = null;\r\n }\r\n };\r\n\r\n return throttled;\r\n}\r\n\r\n/**\r\n * 带尾调的节流 - 立即执行 + 等待结束后再执行最后一次\r\n */\r\nexport function throttleWithTrailing<T extends (...args: unknown[]) => unknown>(\r\n fn: T,\r\n waitMs: number,\r\n): ThrottledFn<T> {\r\n let timer: ReturnType<typeof setTimeout> | null = null;\r\n let lastArgs: Parameters<T> | null = null;\r\n let isThrottled = false;\r\n\r\n const throttled = ((...args: Parameters<T>) => {\r\n lastArgs = args;\r\n if (!isThrottled) {\r\n isThrottled = true;\r\n fn(...args);\r\n timer = setTimeout(() => {\r\n isThrottled = false;\r\n if (lastArgs) {\r\n fn(...lastArgs);\r\n lastArgs = null;\r\n }\r\n timer = null;\r\n }, waitMs);\r\n }\r\n }) as ThrottledFn<T>;\r\n\r\n throttled.cancel = () => {\r\n if (timer) {\r\n clearTimeout(timer);\r\n timer = null;\r\n }\r\n isThrottled = false;\r\n lastArgs = null;\r\n };\r\n\r\n return throttled;\r\n}\r\n\r\n/**\r\n * 延迟指定时间\r\n */\r\nexport function delay<T = void>(ms: number, value?: T): Promise<T> {\r\n return new Promise((resolve) => {\r\n setTimeout(() => resolve(value as T), ms);\r\n });\r\n}\r\n\r\n/**\r\n * 重试函数\r\n */\r\nexport async function retry<T>(\r\n fn: () => Promise<T>,\r\n maxRetries: number = 3,\r\n retryDelay: number = 1000,\r\n retryCondition?: (error: Error) => boolean,\r\n): Promise<T> {\r\n // maxRetries=0 表示不重试,直接执行一次\r\n if (maxRetries <= 0) {\r\n return fn();\r\n }\r\n\r\n let lastError: Error | undefined;\r\n\r\n for (let attempt = 0; attempt < maxRetries; attempt++) {\r\n try {\r\n return await fn();\r\n } catch (err) {\r\n lastError = err instanceof Error ? err : new Error(String(err));\r\n\r\n if (attempt < maxRetries - 1) {\r\n if (retryCondition && !retryCondition(lastError)) {\r\n throw lastError;\r\n }\r\n await delay(retryDelay);\r\n }\r\n }\r\n }\r\n\r\n throw lastError!;\r\n}\r\n\r\n/**\r\n * 超时包装\r\n */\r\nexport function timeout<T>(\r\n promise: Promise<T>,\r\n ms: number,\r\n message: string = 'Timeout',\r\n): Promise<T> {\r\n return new Promise((resolve, reject) => {\r\n const timer = setTimeout(() => {\r\n reject(new Error(message));\r\n }, ms);\r\n\r\n promise.then(\r\n (value) => {\r\n clearTimeout(timer);\r\n resolve(value);\r\n },\r\n (err) => {\r\n clearTimeout(timer);\r\n reject(err);\r\n },\r\n );\r\n });\r\n}\r\n\r\n/**\r\n * 轮询直到条件满足\r\n */\r\nexport async function poll<T>(\r\n fn: () => T | Promise<T>,\r\n condition: (value: T) => boolean,\r\n interval: number = 1000,\r\n timeoutMs: number = 30000,\r\n): Promise<T> {\r\n const startTime = Date.now();\r\n let consecutiveErrors = 0;\r\n const MAX_CONSECUTIVE_ERRORS = 10;\r\n\r\n while (true) {\r\n try {\r\n const value = await fn();\r\n consecutiveErrors = 0;\r\n if (condition(value)) {\r\n return value;\r\n }\r\n } catch (err) {\r\n consecutiveErrors++;\r\n if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {\r\n throw new Error(\r\n `Polling aborted after ${MAX_CONSECUTIVE_ERRORS} consecutive errors: ${err instanceof Error ? err.message : String(err)}`,\r\n );\r\n }\r\n }\r\n\r\n if (Date.now() - startTime >= timeoutMs) {\r\n throw new Error('Polling timeout exceeded');\r\n }\r\n\r\n await delay(interval);\r\n }\r\n}\r\n\r\n/**\r\n * 任务队列 - 控制并发执行的任务队列\r\n */\r\nexport class TaskQueue {\r\n private queue: Array<() => Promise<void>> = [];\r\n private running: number = 0;\r\n private concurrency: number;\r\n private resolvers: Array<() => void> = [];\r\n\r\n constructor(concurrency: number = 1) {\r\n if (concurrency < 1) {\r\n throw new Error('TaskQueue concurrency must be at least 1');\r\n }\r\n this.concurrency = concurrency;\r\n }\r\n\r\n get size(): number {\r\n return this.queue.length;\r\n }\r\n\r\n add(task: () => Promise<void>): void {\r\n this.queue.push(task);\r\n this.run();\r\n }\r\n\r\n private async run(): Promise<void> {\r\n if (this.running >= this.concurrency || this.queue.length === 0) return;\r\n\r\n this.running++;\r\n const task = this.queue.shift()!;\r\n\r\n try {\r\n await task();\r\n } catch (err) {\r\n console.error('TaskQueue task error:', err);\r\n } finally {\r\n this.running--;\r\n if (this.queue.length > 0) {\r\n this.run();\r\n } else if (this.running === 0) {\r\n this.resolvers.forEach((resolve) => resolve());\r\n this.resolvers = [];\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * 等待所有任务完成\r\n */\r\n async wait(): Promise<void> {\r\n if (this.running === 0 && this.queue.length === 0) return;\r\n return new Promise<void>((resolve) => {\r\n this.resolvers.push(resolve);\r\n });\r\n }\r\n\r\n /**\r\n * 清空待执行的任务队列\r\n */\r\n clear(): void {\r\n this.queue = [];\r\n }\r\n}\r\n\r\n// ============================================================\r\n// 函数工具(从 @lytjs/shared 迁移)\r\n// ============================================================\r\n\r\n/**\r\n * 返回自身的恒等函数\r\n *\r\n * @param value - 输入值\r\n * @returns 输入值本身\r\n * @example\r\n * ```ts\r\n * identity(42) // 42\r\n * identity({ a: 1 }) // { a: 1 }\r\n * ```\r\n */\r\nexport function identity<T>(value: T): T {\r\n return value;\r\n}\r\n\r\n/**\r\n * 创建一个始终返回指定值的函数\r\n *\r\n * @param value - 要返回的值\r\n * @returns 返回该值的函数\r\n * @example\r\n * ```ts\r\n * const alwaysTrue = constant(true)\r\n * alwaysTrue() // true\r\n * ```\r\n */\r\nexport function constant<T>(value: T): () => T {\r\n return () => value;\r\n}\r\n\r\n/**\r\n * 只执行一次的函数\r\n *\r\n * FIX: P2-v11-27 改进 once 函数:支持异步函数场景,\r\n * 在异步执行期间阻止重复调用,执行完成后正确返回结果\r\n *\r\n * @param fn - 要执行的函数\r\n * @returns 只执行一次的包装函数\r\n * @example\r\n * ```ts\r\n * const init = once(() => console.log('initialized'))\r\n * init() // 输出 'initialized'\r\n * init() // 无输出\r\n * ```\r\n */\r\nexport function once<T extends (...args: unknown[]) => unknown>(\r\n fn: T,\r\n): (...args: Parameters<T>) => ReturnType<T> | undefined | Promise<ReturnType<T> | undefined> {\r\n let called = false;\r\n let result: ReturnType<T>;\r\n let pendingPromise: Promise<ReturnType<T>> | null = null;\r\n\r\n return (...args: Parameters<T>): ReturnType<T> | undefined | Promise<ReturnType<T> | undefined> => {\r\n if (!called) {\r\n called = true;\r\n try {\r\n result = fn(...args) as ReturnType<T>;\r\n // 如果结果是 Promise,跟踪其状态以防止并发调用\r\n if (result instanceof Promise) {\r\n pendingPromise = result;\r\n return result.then(\r\n (val) => { pendingPromise = null; return val; },\r\n (err) => { pendingPromise = null; throw err; },\r\n ) as Promise<ReturnType<T> | undefined>;\r\n }\r\n return result;\r\n } catch (e) {\r\n // 同步异常:重置 called 标志,允许重试\r\n called = false;\r\n throw e;\r\n }\r\n }\r\n // 如果有正在执行的异步操作,返回其 Promise\r\n if (pendingPromise) return pendingPromise;\r\n return undefined;\r\n };\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * @lytjs/common-timing\n * 定时与调度工具函数\n */\n\n/**\n * 防抖函数返回类型\n */\nexport interface DebouncedFn<T extends (...args: unknown[]) => unknown> {\n (...args: Parameters<T>): void;\n cancel: () => void;\n}\n\n/**\n * 防抖 - 延迟执行,在最后一次调用后等待指定时间\n */\nexport function debounce<T extends (...args: unknown[]) => unknown>(\n fn: T,\n waitMs: number,\n): DebouncedFn<T> {\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n const debounced = ((...args: Parameters<T>) => {\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => {\n fn(...args);\n timer = null;\n }, waitMs);\n }) as DebouncedFn<T>;\n\n debounced.cancel = () => {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n };\n\n return debounced;\n}\n\n/**\n * 立即防抖 - 立即执行一次,然后在等待期间不再执行\n */\nexport function debounceImmediate<T extends (...args: unknown[]) => unknown>(\n fn: T,\n waitMs: number,\n): DebouncedFn<T> {\n let timer: ReturnType<typeof setTimeout> | null = null;\n let isWaiting = false;\n\n const debounced = ((...args: Parameters<T>) => {\n if (!isWaiting) {\n fn(...args);\n isWaiting = true;\n timer = setTimeout(() => {\n isWaiting = false;\n timer = null;\n }, waitMs);\n }\n }) as DebouncedFn<T>;\n\n debounced.cancel = () => {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n isWaiting = false;\n };\n\n return debounced;\n}\n\n/**\n * 节流函数返回类型\n */\nexport interface ThrottledFn<T extends (...args: unknown[]) => unknown> {\n (...args: Parameters<T>): void;\n cancel: () => void;\n}\n\n/**\n * 节流 - 在指定时间内最多执行一次\n *\n * @note 由于浏览器 setTimeout 的最小延迟通常为 4ms(嵌套调用时),\n * 且在后台标签页中可能被进一步节流至 1000ms,实际节流间隔\n * 可能略大于指定的 waitMs。对于高精度定时需求,请考虑使用\n * requestAnimationFrame 或 Web Worker。\n */\nexport function throttle<T extends (...args: unknown[]) => unknown>(\n fn: T,\n waitMs: number,\n): ThrottledFn<T> {\n let timer: ReturnType<typeof setTimeout> | null = null;\n let lastCallTime = 0;\n let cancelled = false;\n\n const throttled = ((...args: Parameters<T>) => {\n if (cancelled) return;\n\n const now = Date.now();\n const elapsed = now - lastCallTime;\n\n if (elapsed >= waitMs) {\n lastCallTime = now;\n fn(...args);\n } else if (!timer) {\n timer = setTimeout(() => {\n lastCallTime = Date.now();\n fn(...args);\n timer = null;\n }, waitMs - elapsed);\n }\n }) as ThrottledFn<T>;\n\n throttled.cancel = () => {\n cancelled = true;\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n };\n\n return throttled;\n}\n\n/**\n * 带尾调的节流 - 立即执行 + 等待结束后再执行最后一次\n */\nexport function throttleWithTrailing<T extends (...args: unknown[]) => unknown>(\n fn: T,\n waitMs: number,\n): ThrottledFn<T> {\n let timer: ReturnType<typeof setTimeout> | null = null;\n let lastArgs: Parameters<T> | null = null;\n let isThrottled = false;\n\n const throttled = ((...args: Parameters<T>) => {\n lastArgs = args;\n if (!isThrottled) {\n isThrottled = true;\n fn(...args);\n timer = setTimeout(() => {\n isThrottled = false;\n if (lastArgs) {\n fn(...lastArgs);\n lastArgs = null;\n }\n timer = null;\n }, waitMs);\n }\n }) as ThrottledFn<T>;\n\n throttled.cancel = () => {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n isThrottled = false;\n lastArgs = null;\n };\n\n return throttled;\n}\n\n/**\n * 延迟指定时间\n */\nexport function delay<T = void>(ms: number, value?: T): Promise<T> {\n return new Promise((resolve) => {\n setTimeout(() => resolve(value as T), ms);\n });\n}\n\n/**\n * 重试函数\n */\nexport async function retry<T>(\n fn: () => Promise<T>,\n maxRetries: number = 3,\n retryDelay: number = 1000,\n retryCondition?: (error: Error) => boolean,\n): Promise<T> {\n // maxRetries=0 表示不重试,直接执行一次\n if (maxRetries <= 0) {\n return fn();\n }\n\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n return await fn();\n } catch (err) {\n lastError = err instanceof Error ? err : new Error(String(err));\n\n if (attempt < maxRetries - 1) {\n if (retryCondition && !retryCondition(lastError)) {\n throw lastError;\n }\n await delay(retryDelay);\n }\n }\n }\n\n throw lastError!;\n}\n\n/**\n * 超时包装\n */\nexport function timeout<T>(\n promise: Promise<T>,\n ms: number,\n message: string = 'Timeout',\n): Promise<T> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n reject(new Error(message));\n }, ms);\n\n promise.then(\n (value) => {\n clearTimeout(timer);\n resolve(value);\n },\n (err) => {\n clearTimeout(timer);\n reject(err);\n },\n );\n });\n}\n\n/**\n * 轮询直到条件满足\n */\nexport async function poll<T>(\n fn: () => T | Promise<T>,\n condition: (value: T) => boolean,\n interval: number = 1000,\n timeoutMs: number = 30000,\n): Promise<T> {\n const startTime = Date.now();\n let consecutiveErrors = 0;\n const MAX_CONSECUTIVE_ERRORS = 10;\n\n while (true) {\n try {\n const value = await fn();\n consecutiveErrors = 0;\n if (condition(value)) {\n return value;\n }\n } catch (err) {\n consecutiveErrors++;\n if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {\n throw new Error(\n `Polling aborted after ${MAX_CONSECUTIVE_ERRORS} consecutive errors: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n if (Date.now() - startTime >= timeoutMs) {\n throw new Error('Polling timeout exceeded');\n }\n\n await delay(interval);\n }\n}\n\n/**\n * 任务队列 - 控制并发执行的任务队列\n */\nexport class TaskQueue {\n private queue: Array<() => Promise<void>> = [];\n private running: number = 0;\n private concurrency: number;\n private resolvers: Array<() => void> = [];\n\n constructor(concurrency: number = 1) {\n if (concurrency < 1) {\n throw new Error('TaskQueue concurrency must be at least 1');\n }\n this.concurrency = concurrency;\n }\n\n get size(): number {\n return this.queue.length;\n }\n\n add(task: () => Promise<void>): void {\n this.queue.push(task);\n this.run();\n }\n\n private async run(): Promise<void> {\n if (this.running >= this.concurrency || this.queue.length === 0) return;\n\n this.running++;\n const task = this.queue.shift()!;\n\n try {\n await task();\n } catch (err) {\n console.error('TaskQueue task error:', err);\n } finally {\n this.running--;\n if (this.queue.length > 0) {\n this.run();\n } else if (this.running === 0) {\n this.resolvers.forEach((resolve) => resolve());\n this.resolvers = [];\n }\n }\n }\n\n /**\n * 等待所有任务完成\n */\n async wait(): Promise<void> {\n if (this.running === 0 && this.queue.length === 0) return;\n return new Promise<void>((resolve) => {\n this.resolvers.push(resolve);\n });\n }\n\n /**\n * 清空待执行的任务队列\n */\n clear(): void {\n this.queue = [];\n }\n}\n\n// ============================================================\n// 函数工具(从 @lytjs/shared 迁移)\n// ============================================================\n\n/**\n * 返回自身的恒等函数\n *\n * @param value - 输入值\n * @returns 输入值本身\n * @example\n * ```ts\n * identity(42) // 42\n * identity({ a: 1 }) // { a: 1 }\n * ```\n */\nexport function identity<T>(value: T): T {\n return value;\n}\n\n/**\n * 创建一个始终返回指定值的函数\n *\n * @param value - 要返回的值\n * @returns 返回该值的函数\n * @example\n * ```ts\n * const alwaysTrue = constant(true)\n * alwaysTrue() // true\n * ```\n */\nexport function constant<T>(value: T): () => T {\n return () => value;\n}\n\n/**\n * 只执行一次的函数\n *\n * FIX: P2-v11-27 改进 once 函数:支持异步函数场景,\n * 在异步执行期间阻止重复调用,执行完成后正确返回结果\n *\n * @param fn - 要执行的函数\n * @returns 只执行一次的包装函数\n * @example\n * ```ts\n * const init = once(() => console.log('initialized'))\n * init() // 输出 'initialized'\n * init() // 无输出\n * ```\n */\nexport function once<T extends (...args: unknown[]) => unknown>(\n fn: T,\n): (...args: Parameters<T>) => ReturnType<T> | undefined | Promise<ReturnType<T> | undefined> {\n let called = false;\n let result: ReturnType<T>;\n let pendingPromise: Promise<ReturnType<T>> | null = null;\n\n return (\n ...args: Parameters<T>\n ): ReturnType<T> | undefined | Promise<ReturnType<T> | undefined> => {\n if (!called) {\n called = true;\n try {\n result = fn(...args) as ReturnType<T>;\n // 如果结果是 Promise,跟踪其状态以防止并发调用\n if (result instanceof Promise) {\n pendingPromise = result;\n return result.then(\n (val) => {\n pendingPromise = null;\n return val;\n },\n (err) => {\n pendingPromise = null;\n throw err;\n },\n ) as Promise<ReturnType<T> | undefined>;\n }\n return result;\n } catch (e) {\n // 同步异常:重置 called 标志,允许重试\n called = false;\n throw e;\n }\n }\n // 如果有正在执行的异步操作,返回其 Promise\n if (pendingPromise) return pendingPromise;\n return undefined;\n };\n}\n"]}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,415 +1,423 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @lytjs/common-timing
|
|
3
|
-
* 定时与调度工具函数
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* 防抖函数返回类型
|
|
8
|
-
*/
|
|
9
|
-
export interface DebouncedFn<T extends (...args: unknown[]) => unknown> {
|
|
10
|
-
(...args: Parameters<T>): void;
|
|
11
|
-
cancel: () => void;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* 防抖 - 延迟执行,在最后一次调用后等待指定时间
|
|
16
|
-
*/
|
|
17
|
-
export function debounce<T extends (...args: unknown[]) => unknown>(
|
|
18
|
-
fn: T,
|
|
19
|
-
waitMs: number,
|
|
20
|
-
): DebouncedFn<T> {
|
|
21
|
-
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
22
|
-
|
|
23
|
-
const debounced = ((...args: Parameters<T>) => {
|
|
24
|
-
if (timer) clearTimeout(timer);
|
|
25
|
-
timer = setTimeout(() => {
|
|
26
|
-
fn(...args);
|
|
27
|
-
timer = null;
|
|
28
|
-
}, waitMs);
|
|
29
|
-
}) as DebouncedFn<T>;
|
|
30
|
-
|
|
31
|
-
debounced.cancel = () => {
|
|
32
|
-
if (timer) {
|
|
33
|
-
clearTimeout(timer);
|
|
34
|
-
timer = null;
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
return debounced;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* 立即防抖 - 立即执行一次,然后在等待期间不再执行
|
|
43
|
-
*/
|
|
44
|
-
export function debounceImmediate<T extends (...args: unknown[]) => unknown>(
|
|
45
|
-
fn: T,
|
|
46
|
-
waitMs: number,
|
|
47
|
-
): DebouncedFn<T> {
|
|
48
|
-
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
49
|
-
let isWaiting = false;
|
|
50
|
-
|
|
51
|
-
const debounced = ((...args: Parameters<T>) => {
|
|
52
|
-
if (!isWaiting) {
|
|
53
|
-
fn(...args);
|
|
54
|
-
isWaiting = true;
|
|
55
|
-
timer = setTimeout(() => {
|
|
56
|
-
isWaiting = false;
|
|
57
|
-
timer = null;
|
|
58
|
-
}, waitMs);
|
|
59
|
-
}
|
|
60
|
-
}) as DebouncedFn<T>;
|
|
61
|
-
|
|
62
|
-
debounced.cancel = () => {
|
|
63
|
-
if (timer) {
|
|
64
|
-
clearTimeout(timer);
|
|
65
|
-
timer = null;
|
|
66
|
-
}
|
|
67
|
-
isWaiting = false;
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
return debounced;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* 节流函数返回类型
|
|
75
|
-
*/
|
|
76
|
-
export interface ThrottledFn<T extends (...args: unknown[]) => unknown> {
|
|
77
|
-
(...args: Parameters<T>): void;
|
|
78
|
-
cancel: () => void;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* 节流 - 在指定时间内最多执行一次
|
|
83
|
-
*
|
|
84
|
-
* @note 由于浏览器 setTimeout 的最小延迟通常为 4ms(嵌套调用时),
|
|
85
|
-
* 且在后台标签页中可能被进一步节流至 1000ms,实际节流间隔
|
|
86
|
-
* 可能略大于指定的 waitMs。对于高精度定时需求,请考虑使用
|
|
87
|
-
* requestAnimationFrame 或 Web Worker。
|
|
88
|
-
*/
|
|
89
|
-
export function throttle<T extends (...args: unknown[]) => unknown>(
|
|
90
|
-
fn: T,
|
|
91
|
-
waitMs: number,
|
|
92
|
-
): ThrottledFn<T> {
|
|
93
|
-
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
94
|
-
let lastCallTime = 0;
|
|
95
|
-
let cancelled = false;
|
|
96
|
-
|
|
97
|
-
const throttled = ((...args: Parameters<T>) => {
|
|
98
|
-
if (cancelled) return;
|
|
99
|
-
|
|
100
|
-
const now = Date.now();
|
|
101
|
-
const elapsed = now - lastCallTime;
|
|
102
|
-
|
|
103
|
-
if (elapsed >= waitMs) {
|
|
104
|
-
lastCallTime = now;
|
|
105
|
-
fn(...args);
|
|
106
|
-
} else if (!timer) {
|
|
107
|
-
timer = setTimeout(() => {
|
|
108
|
-
lastCallTime = Date.now();
|
|
109
|
-
fn(...args);
|
|
110
|
-
timer = null;
|
|
111
|
-
}, waitMs - elapsed);
|
|
112
|
-
}
|
|
113
|
-
}) as ThrottledFn<T>;
|
|
114
|
-
|
|
115
|
-
throttled.cancel = () => {
|
|
116
|
-
cancelled = true;
|
|
117
|
-
if (timer) {
|
|
118
|
-
clearTimeout(timer);
|
|
119
|
-
timer = null;
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
return throttled;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* 带尾调的节流 - 立即执行 + 等待结束后再执行最后一次
|
|
128
|
-
*/
|
|
129
|
-
export function throttleWithTrailing<T extends (...args: unknown[]) => unknown>(
|
|
130
|
-
fn: T,
|
|
131
|
-
waitMs: number,
|
|
132
|
-
): ThrottledFn<T> {
|
|
133
|
-
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
134
|
-
let lastArgs: Parameters<T> | null = null;
|
|
135
|
-
let isThrottled = false;
|
|
136
|
-
|
|
137
|
-
const throttled = ((...args: Parameters<T>) => {
|
|
138
|
-
lastArgs = args;
|
|
139
|
-
if (!isThrottled) {
|
|
140
|
-
isThrottled = true;
|
|
141
|
-
fn(...args);
|
|
142
|
-
timer = setTimeout(() => {
|
|
143
|
-
isThrottled = false;
|
|
144
|
-
if (lastArgs) {
|
|
145
|
-
fn(...lastArgs);
|
|
146
|
-
lastArgs = null;
|
|
147
|
-
}
|
|
148
|
-
timer = null;
|
|
149
|
-
}, waitMs);
|
|
150
|
-
}
|
|
151
|
-
}) as ThrottledFn<T>;
|
|
152
|
-
|
|
153
|
-
throttled.cancel = () => {
|
|
154
|
-
if (timer) {
|
|
155
|
-
clearTimeout(timer);
|
|
156
|
-
timer = null;
|
|
157
|
-
}
|
|
158
|
-
isThrottled = false;
|
|
159
|
-
lastArgs = null;
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
return throttled;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* 延迟指定时间
|
|
167
|
-
*/
|
|
168
|
-
export function delay<T = void>(ms: number, value?: T): Promise<T> {
|
|
169
|
-
return new Promise((resolve) => {
|
|
170
|
-
setTimeout(() => resolve(value as T), ms);
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* 重试函数
|
|
176
|
-
*/
|
|
177
|
-
export async function retry<T>(
|
|
178
|
-
fn: () => Promise<T>,
|
|
179
|
-
maxRetries: number = 3,
|
|
180
|
-
retryDelay: number = 1000,
|
|
181
|
-
retryCondition?: (error: Error) => boolean,
|
|
182
|
-
): Promise<T> {
|
|
183
|
-
// maxRetries=0 表示不重试,直接执行一次
|
|
184
|
-
if (maxRetries <= 0) {
|
|
185
|
-
return fn();
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
let lastError: Error | undefined;
|
|
189
|
-
|
|
190
|
-
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
191
|
-
try {
|
|
192
|
-
return await fn();
|
|
193
|
-
} catch (err) {
|
|
194
|
-
lastError = err instanceof Error ? err : new Error(String(err));
|
|
195
|
-
|
|
196
|
-
if (attempt < maxRetries - 1) {
|
|
197
|
-
if (retryCondition && !retryCondition(lastError)) {
|
|
198
|
-
throw lastError;
|
|
199
|
-
}
|
|
200
|
-
await delay(retryDelay);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
throw lastError!;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* 超时包装
|
|
210
|
-
*/
|
|
211
|
-
export function timeout<T>(
|
|
212
|
-
promise: Promise<T>,
|
|
213
|
-
ms: number,
|
|
214
|
-
message: string = 'Timeout',
|
|
215
|
-
): Promise<T> {
|
|
216
|
-
return new Promise((resolve, reject) => {
|
|
217
|
-
const timer = setTimeout(() => {
|
|
218
|
-
reject(new Error(message));
|
|
219
|
-
}, ms);
|
|
220
|
-
|
|
221
|
-
promise.then(
|
|
222
|
-
(value) => {
|
|
223
|
-
clearTimeout(timer);
|
|
224
|
-
resolve(value);
|
|
225
|
-
},
|
|
226
|
-
(err) => {
|
|
227
|
-
clearTimeout(timer);
|
|
228
|
-
reject(err);
|
|
229
|
-
},
|
|
230
|
-
);
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* 轮询直到条件满足
|
|
236
|
-
*/
|
|
237
|
-
export async function poll<T>(
|
|
238
|
-
fn: () => T | Promise<T>,
|
|
239
|
-
condition: (value: T) => boolean,
|
|
240
|
-
interval: number = 1000,
|
|
241
|
-
timeoutMs: number = 30000,
|
|
242
|
-
): Promise<T> {
|
|
243
|
-
const startTime = Date.now();
|
|
244
|
-
let consecutiveErrors = 0;
|
|
245
|
-
const MAX_CONSECUTIVE_ERRORS = 10;
|
|
246
|
-
|
|
247
|
-
while (true) {
|
|
248
|
-
try {
|
|
249
|
-
const value = await fn();
|
|
250
|
-
consecutiveErrors = 0;
|
|
251
|
-
if (condition(value)) {
|
|
252
|
-
return value;
|
|
253
|
-
}
|
|
254
|
-
} catch (err) {
|
|
255
|
-
consecutiveErrors++;
|
|
256
|
-
if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
|
|
257
|
-
throw new Error(
|
|
258
|
-
`Polling aborted after ${MAX_CONSECUTIVE_ERRORS} consecutive errors: ${err instanceof Error ? err.message : String(err)}`,
|
|
259
|
-
);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
if (Date.now() - startTime >= timeoutMs) {
|
|
264
|
-
throw new Error('Polling timeout exceeded');
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
await delay(interval);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* 任务队列 - 控制并发执行的任务队列
|
|
273
|
-
*/
|
|
274
|
-
export class TaskQueue {
|
|
275
|
-
private queue: Array<() => Promise<void>> = [];
|
|
276
|
-
private running: number = 0;
|
|
277
|
-
private concurrency: number;
|
|
278
|
-
private resolvers: Array<() => void> = [];
|
|
279
|
-
|
|
280
|
-
constructor(concurrency: number = 1) {
|
|
281
|
-
if (concurrency < 1) {
|
|
282
|
-
throw new Error('TaskQueue concurrency must be at least 1');
|
|
283
|
-
}
|
|
284
|
-
this.concurrency = concurrency;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
get size(): number {
|
|
288
|
-
return this.queue.length;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
add(task: () => Promise<void>): void {
|
|
292
|
-
this.queue.push(task);
|
|
293
|
-
this.run();
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
private async run(): Promise<void> {
|
|
297
|
-
if (this.running >= this.concurrency || this.queue.length === 0) return;
|
|
298
|
-
|
|
299
|
-
this.running++;
|
|
300
|
-
const task = this.queue.shift()!;
|
|
301
|
-
|
|
302
|
-
try {
|
|
303
|
-
await task();
|
|
304
|
-
} catch (err) {
|
|
305
|
-
console.error('TaskQueue task error:', err);
|
|
306
|
-
} finally {
|
|
307
|
-
this.running--;
|
|
308
|
-
if (this.queue.length > 0) {
|
|
309
|
-
this.run();
|
|
310
|
-
} else if (this.running === 0) {
|
|
311
|
-
this.resolvers.forEach((resolve) => resolve());
|
|
312
|
-
this.resolvers = [];
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* 等待所有任务完成
|
|
319
|
-
*/
|
|
320
|
-
async wait(): Promise<void> {
|
|
321
|
-
if (this.running === 0 && this.queue.length === 0) return;
|
|
322
|
-
return new Promise<void>((resolve) => {
|
|
323
|
-
this.resolvers.push(resolve);
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* 清空待执行的任务队列
|
|
329
|
-
*/
|
|
330
|
-
clear(): void {
|
|
331
|
-
this.queue = [];
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// ============================================================
|
|
336
|
-
// 函数工具(从 @lytjs/shared 迁移)
|
|
337
|
-
// ============================================================
|
|
338
|
-
|
|
339
|
-
/**
|
|
340
|
-
* 返回自身的恒等函数
|
|
341
|
-
*
|
|
342
|
-
* @param value - 输入值
|
|
343
|
-
* @returns 输入值本身
|
|
344
|
-
* @example
|
|
345
|
-
* ```ts
|
|
346
|
-
* identity(42) // 42
|
|
347
|
-
* identity({ a: 1 }) // { a: 1 }
|
|
348
|
-
* ```
|
|
349
|
-
*/
|
|
350
|
-
export function identity<T>(value: T): T {
|
|
351
|
-
return value;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
/**
|
|
355
|
-
* 创建一个始终返回指定值的函数
|
|
356
|
-
*
|
|
357
|
-
* @param value - 要返回的值
|
|
358
|
-
* @returns 返回该值的函数
|
|
359
|
-
* @example
|
|
360
|
-
* ```ts
|
|
361
|
-
* const alwaysTrue = constant(true)
|
|
362
|
-
* alwaysTrue() // true
|
|
363
|
-
* ```
|
|
364
|
-
*/
|
|
365
|
-
export function constant<T>(value: T): () => T {
|
|
366
|
-
return () => value;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* 只执行一次的函数
|
|
371
|
-
*
|
|
372
|
-
* FIX: P2-v11-27 改进 once 函数:支持异步函数场景,
|
|
373
|
-
* 在异步执行期间阻止重复调用,执行完成后正确返回结果
|
|
374
|
-
*
|
|
375
|
-
* @param fn - 要执行的函数
|
|
376
|
-
* @returns 只执行一次的包装函数
|
|
377
|
-
* @example
|
|
378
|
-
* ```ts
|
|
379
|
-
* const init = once(() => console.log('initialized'))
|
|
380
|
-
* init() // 输出 'initialized'
|
|
381
|
-
* init() // 无输出
|
|
382
|
-
* ```
|
|
383
|
-
*/
|
|
384
|
-
export function once<T extends (...args: unknown[]) => unknown>(
|
|
385
|
-
fn: T,
|
|
386
|
-
): (...args: Parameters<T>) => ReturnType<T> | undefined | Promise<ReturnType<T> | undefined> {
|
|
387
|
-
let called = false;
|
|
388
|
-
let result: ReturnType<T>;
|
|
389
|
-
let pendingPromise: Promise<ReturnType<T>> | null = null;
|
|
390
|
-
|
|
391
|
-
return (
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @lytjs/common-timing
|
|
3
|
+
* 定时与调度工具函数
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 防抖函数返回类型
|
|
8
|
+
*/
|
|
9
|
+
export interface DebouncedFn<T extends (...args: unknown[]) => unknown> {
|
|
10
|
+
(...args: Parameters<T>): void;
|
|
11
|
+
cancel: () => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 防抖 - 延迟执行,在最后一次调用后等待指定时间
|
|
16
|
+
*/
|
|
17
|
+
export function debounce<T extends (...args: unknown[]) => unknown>(
|
|
18
|
+
fn: T,
|
|
19
|
+
waitMs: number,
|
|
20
|
+
): DebouncedFn<T> {
|
|
21
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
22
|
+
|
|
23
|
+
const debounced = ((...args: Parameters<T>) => {
|
|
24
|
+
if (timer) clearTimeout(timer);
|
|
25
|
+
timer = setTimeout(() => {
|
|
26
|
+
fn(...args);
|
|
27
|
+
timer = null;
|
|
28
|
+
}, waitMs);
|
|
29
|
+
}) as DebouncedFn<T>;
|
|
30
|
+
|
|
31
|
+
debounced.cancel = () => {
|
|
32
|
+
if (timer) {
|
|
33
|
+
clearTimeout(timer);
|
|
34
|
+
timer = null;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
return debounced;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 立即防抖 - 立即执行一次,然后在等待期间不再执行
|
|
43
|
+
*/
|
|
44
|
+
export function debounceImmediate<T extends (...args: unknown[]) => unknown>(
|
|
45
|
+
fn: T,
|
|
46
|
+
waitMs: number,
|
|
47
|
+
): DebouncedFn<T> {
|
|
48
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
49
|
+
let isWaiting = false;
|
|
50
|
+
|
|
51
|
+
const debounced = ((...args: Parameters<T>) => {
|
|
52
|
+
if (!isWaiting) {
|
|
53
|
+
fn(...args);
|
|
54
|
+
isWaiting = true;
|
|
55
|
+
timer = setTimeout(() => {
|
|
56
|
+
isWaiting = false;
|
|
57
|
+
timer = null;
|
|
58
|
+
}, waitMs);
|
|
59
|
+
}
|
|
60
|
+
}) as DebouncedFn<T>;
|
|
61
|
+
|
|
62
|
+
debounced.cancel = () => {
|
|
63
|
+
if (timer) {
|
|
64
|
+
clearTimeout(timer);
|
|
65
|
+
timer = null;
|
|
66
|
+
}
|
|
67
|
+
isWaiting = false;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
return debounced;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 节流函数返回类型
|
|
75
|
+
*/
|
|
76
|
+
export interface ThrottledFn<T extends (...args: unknown[]) => unknown> {
|
|
77
|
+
(...args: Parameters<T>): void;
|
|
78
|
+
cancel: () => void;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 节流 - 在指定时间内最多执行一次
|
|
83
|
+
*
|
|
84
|
+
* @note 由于浏览器 setTimeout 的最小延迟通常为 4ms(嵌套调用时),
|
|
85
|
+
* 且在后台标签页中可能被进一步节流至 1000ms,实际节流间隔
|
|
86
|
+
* 可能略大于指定的 waitMs。对于高精度定时需求,请考虑使用
|
|
87
|
+
* requestAnimationFrame 或 Web Worker。
|
|
88
|
+
*/
|
|
89
|
+
export function throttle<T extends (...args: unknown[]) => unknown>(
|
|
90
|
+
fn: T,
|
|
91
|
+
waitMs: number,
|
|
92
|
+
): ThrottledFn<T> {
|
|
93
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
94
|
+
let lastCallTime = 0;
|
|
95
|
+
let cancelled = false;
|
|
96
|
+
|
|
97
|
+
const throttled = ((...args: Parameters<T>) => {
|
|
98
|
+
if (cancelled) return;
|
|
99
|
+
|
|
100
|
+
const now = Date.now();
|
|
101
|
+
const elapsed = now - lastCallTime;
|
|
102
|
+
|
|
103
|
+
if (elapsed >= waitMs) {
|
|
104
|
+
lastCallTime = now;
|
|
105
|
+
fn(...args);
|
|
106
|
+
} else if (!timer) {
|
|
107
|
+
timer = setTimeout(() => {
|
|
108
|
+
lastCallTime = Date.now();
|
|
109
|
+
fn(...args);
|
|
110
|
+
timer = null;
|
|
111
|
+
}, waitMs - elapsed);
|
|
112
|
+
}
|
|
113
|
+
}) as ThrottledFn<T>;
|
|
114
|
+
|
|
115
|
+
throttled.cancel = () => {
|
|
116
|
+
cancelled = true;
|
|
117
|
+
if (timer) {
|
|
118
|
+
clearTimeout(timer);
|
|
119
|
+
timer = null;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
return throttled;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* 带尾调的节流 - 立即执行 + 等待结束后再执行最后一次
|
|
128
|
+
*/
|
|
129
|
+
export function throttleWithTrailing<T extends (...args: unknown[]) => unknown>(
|
|
130
|
+
fn: T,
|
|
131
|
+
waitMs: number,
|
|
132
|
+
): ThrottledFn<T> {
|
|
133
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
134
|
+
let lastArgs: Parameters<T> | null = null;
|
|
135
|
+
let isThrottled = false;
|
|
136
|
+
|
|
137
|
+
const throttled = ((...args: Parameters<T>) => {
|
|
138
|
+
lastArgs = args;
|
|
139
|
+
if (!isThrottled) {
|
|
140
|
+
isThrottled = true;
|
|
141
|
+
fn(...args);
|
|
142
|
+
timer = setTimeout(() => {
|
|
143
|
+
isThrottled = false;
|
|
144
|
+
if (lastArgs) {
|
|
145
|
+
fn(...lastArgs);
|
|
146
|
+
lastArgs = null;
|
|
147
|
+
}
|
|
148
|
+
timer = null;
|
|
149
|
+
}, waitMs);
|
|
150
|
+
}
|
|
151
|
+
}) as ThrottledFn<T>;
|
|
152
|
+
|
|
153
|
+
throttled.cancel = () => {
|
|
154
|
+
if (timer) {
|
|
155
|
+
clearTimeout(timer);
|
|
156
|
+
timer = null;
|
|
157
|
+
}
|
|
158
|
+
isThrottled = false;
|
|
159
|
+
lastArgs = null;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
return throttled;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* 延迟指定时间
|
|
167
|
+
*/
|
|
168
|
+
export function delay<T = void>(ms: number, value?: T): Promise<T> {
|
|
169
|
+
return new Promise((resolve) => {
|
|
170
|
+
setTimeout(() => resolve(value as T), ms);
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* 重试函数
|
|
176
|
+
*/
|
|
177
|
+
export async function retry<T>(
|
|
178
|
+
fn: () => Promise<T>,
|
|
179
|
+
maxRetries: number = 3,
|
|
180
|
+
retryDelay: number = 1000,
|
|
181
|
+
retryCondition?: (error: Error) => boolean,
|
|
182
|
+
): Promise<T> {
|
|
183
|
+
// maxRetries=0 表示不重试,直接执行一次
|
|
184
|
+
if (maxRetries <= 0) {
|
|
185
|
+
return fn();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
let lastError: Error | undefined;
|
|
189
|
+
|
|
190
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
191
|
+
try {
|
|
192
|
+
return await fn();
|
|
193
|
+
} catch (err) {
|
|
194
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
195
|
+
|
|
196
|
+
if (attempt < maxRetries - 1) {
|
|
197
|
+
if (retryCondition && !retryCondition(lastError)) {
|
|
198
|
+
throw lastError;
|
|
199
|
+
}
|
|
200
|
+
await delay(retryDelay);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
throw lastError!;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* 超时包装
|
|
210
|
+
*/
|
|
211
|
+
export function timeout<T>(
|
|
212
|
+
promise: Promise<T>,
|
|
213
|
+
ms: number,
|
|
214
|
+
message: string = 'Timeout',
|
|
215
|
+
): Promise<T> {
|
|
216
|
+
return new Promise((resolve, reject) => {
|
|
217
|
+
const timer = setTimeout(() => {
|
|
218
|
+
reject(new Error(message));
|
|
219
|
+
}, ms);
|
|
220
|
+
|
|
221
|
+
promise.then(
|
|
222
|
+
(value) => {
|
|
223
|
+
clearTimeout(timer);
|
|
224
|
+
resolve(value);
|
|
225
|
+
},
|
|
226
|
+
(err) => {
|
|
227
|
+
clearTimeout(timer);
|
|
228
|
+
reject(err);
|
|
229
|
+
},
|
|
230
|
+
);
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* 轮询直到条件满足
|
|
236
|
+
*/
|
|
237
|
+
export async function poll<T>(
|
|
238
|
+
fn: () => T | Promise<T>,
|
|
239
|
+
condition: (value: T) => boolean,
|
|
240
|
+
interval: number = 1000,
|
|
241
|
+
timeoutMs: number = 30000,
|
|
242
|
+
): Promise<T> {
|
|
243
|
+
const startTime = Date.now();
|
|
244
|
+
let consecutiveErrors = 0;
|
|
245
|
+
const MAX_CONSECUTIVE_ERRORS = 10;
|
|
246
|
+
|
|
247
|
+
while (true) {
|
|
248
|
+
try {
|
|
249
|
+
const value = await fn();
|
|
250
|
+
consecutiveErrors = 0;
|
|
251
|
+
if (condition(value)) {
|
|
252
|
+
return value;
|
|
253
|
+
}
|
|
254
|
+
} catch (err) {
|
|
255
|
+
consecutiveErrors++;
|
|
256
|
+
if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
|
|
257
|
+
throw new Error(
|
|
258
|
+
`Polling aborted after ${MAX_CONSECUTIVE_ERRORS} consecutive errors: ${err instanceof Error ? err.message : String(err)}`,
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (Date.now() - startTime >= timeoutMs) {
|
|
264
|
+
throw new Error('Polling timeout exceeded');
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
await delay(interval);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* 任务队列 - 控制并发执行的任务队列
|
|
273
|
+
*/
|
|
274
|
+
export class TaskQueue {
|
|
275
|
+
private queue: Array<() => Promise<void>> = [];
|
|
276
|
+
private running: number = 0;
|
|
277
|
+
private concurrency: number;
|
|
278
|
+
private resolvers: Array<() => void> = [];
|
|
279
|
+
|
|
280
|
+
constructor(concurrency: number = 1) {
|
|
281
|
+
if (concurrency < 1) {
|
|
282
|
+
throw new Error('TaskQueue concurrency must be at least 1');
|
|
283
|
+
}
|
|
284
|
+
this.concurrency = concurrency;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
get size(): number {
|
|
288
|
+
return this.queue.length;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
add(task: () => Promise<void>): void {
|
|
292
|
+
this.queue.push(task);
|
|
293
|
+
this.run();
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
private async run(): Promise<void> {
|
|
297
|
+
if (this.running >= this.concurrency || this.queue.length === 0) return;
|
|
298
|
+
|
|
299
|
+
this.running++;
|
|
300
|
+
const task = this.queue.shift()!;
|
|
301
|
+
|
|
302
|
+
try {
|
|
303
|
+
await task();
|
|
304
|
+
} catch (err) {
|
|
305
|
+
console.error('TaskQueue task error:', err);
|
|
306
|
+
} finally {
|
|
307
|
+
this.running--;
|
|
308
|
+
if (this.queue.length > 0) {
|
|
309
|
+
this.run();
|
|
310
|
+
} else if (this.running === 0) {
|
|
311
|
+
this.resolvers.forEach((resolve) => resolve());
|
|
312
|
+
this.resolvers = [];
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* 等待所有任务完成
|
|
319
|
+
*/
|
|
320
|
+
async wait(): Promise<void> {
|
|
321
|
+
if (this.running === 0 && this.queue.length === 0) return;
|
|
322
|
+
return new Promise<void>((resolve) => {
|
|
323
|
+
this.resolvers.push(resolve);
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* 清空待执行的任务队列
|
|
329
|
+
*/
|
|
330
|
+
clear(): void {
|
|
331
|
+
this.queue = [];
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// ============================================================
|
|
336
|
+
// 函数工具(从 @lytjs/shared 迁移)
|
|
337
|
+
// ============================================================
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* 返回自身的恒等函数
|
|
341
|
+
*
|
|
342
|
+
* @param value - 输入值
|
|
343
|
+
* @returns 输入值本身
|
|
344
|
+
* @example
|
|
345
|
+
* ```ts
|
|
346
|
+
* identity(42) // 42
|
|
347
|
+
* identity({ a: 1 }) // { a: 1 }
|
|
348
|
+
* ```
|
|
349
|
+
*/
|
|
350
|
+
export function identity<T>(value: T): T {
|
|
351
|
+
return value;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* 创建一个始终返回指定值的函数
|
|
356
|
+
*
|
|
357
|
+
* @param value - 要返回的值
|
|
358
|
+
* @returns 返回该值的函数
|
|
359
|
+
* @example
|
|
360
|
+
* ```ts
|
|
361
|
+
* const alwaysTrue = constant(true)
|
|
362
|
+
* alwaysTrue() // true
|
|
363
|
+
* ```
|
|
364
|
+
*/
|
|
365
|
+
export function constant<T>(value: T): () => T {
|
|
366
|
+
return () => value;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* 只执行一次的函数
|
|
371
|
+
*
|
|
372
|
+
* FIX: P2-v11-27 改进 once 函数:支持异步函数场景,
|
|
373
|
+
* 在异步执行期间阻止重复调用,执行完成后正确返回结果
|
|
374
|
+
*
|
|
375
|
+
* @param fn - 要执行的函数
|
|
376
|
+
* @returns 只执行一次的包装函数
|
|
377
|
+
* @example
|
|
378
|
+
* ```ts
|
|
379
|
+
* const init = once(() => console.log('initialized'))
|
|
380
|
+
* init() // 输出 'initialized'
|
|
381
|
+
* init() // 无输出
|
|
382
|
+
* ```
|
|
383
|
+
*/
|
|
384
|
+
export function once<T extends (...args: unknown[]) => unknown>(
|
|
385
|
+
fn: T,
|
|
386
|
+
): (...args: Parameters<T>) => ReturnType<T> | undefined | Promise<ReturnType<T> | undefined> {
|
|
387
|
+
let called = false;
|
|
388
|
+
let result: ReturnType<T>;
|
|
389
|
+
let pendingPromise: Promise<ReturnType<T>> | null = null;
|
|
390
|
+
|
|
391
|
+
return (
|
|
392
|
+
...args: Parameters<T>
|
|
393
|
+
): ReturnType<T> | undefined | Promise<ReturnType<T> | undefined> => {
|
|
394
|
+
if (!called) {
|
|
395
|
+
called = true;
|
|
396
|
+
try {
|
|
397
|
+
result = fn(...args) as ReturnType<T>;
|
|
398
|
+
// 如果结果是 Promise,跟踪其状态以防止并发调用
|
|
399
|
+
if (result instanceof Promise) {
|
|
400
|
+
pendingPromise = result;
|
|
401
|
+
return result.then(
|
|
402
|
+
(val) => {
|
|
403
|
+
pendingPromise = null;
|
|
404
|
+
return val;
|
|
405
|
+
},
|
|
406
|
+
(err) => {
|
|
407
|
+
pendingPromise = null;
|
|
408
|
+
throw err;
|
|
409
|
+
},
|
|
410
|
+
) as Promise<ReturnType<T> | undefined>;
|
|
411
|
+
}
|
|
412
|
+
return result;
|
|
413
|
+
} catch (e) {
|
|
414
|
+
// 同步异常:重置 called 标志,允许重试
|
|
415
|
+
called = false;
|
|
416
|
+
throw e;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
// 如果有正在执行的异步操作,返回其 Promise
|
|
420
|
+
if (pendingPromise) return pendingPromise;
|
|
421
|
+
return undefined;
|
|
422
|
+
};
|
|
423
|
+
}
|