@cloudcome/utils-core 1.20.1 → 1.22.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/function.cjs CHANGED
@@ -1,5 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_type = require("./type.cjs");
3
+ const require_promise = require("./promise.cjs");
3
4
  //#region src/function.ts
4
5
  /**
5
6
  * 一个空操作函数,不执行任何操作。
@@ -130,10 +131,42 @@ function fnOnce(fn) {
130
131
  return result;
131
132
  };
132
133
  }
134
+ /**
135
+ * 创建一个带重试能力的函数,失败时自动重试直到成功或耗尽次数。
136
+ *
137
+ * @param fn - 需要重试的函数。
138
+ * @param options - 重试配置选项。
139
+ * @returns 返回一个带重试能力的函数。
140
+ *
141
+ * @example
142
+ * ```typescript
143
+ * const fetchData = fnRetry(async () => {
144
+ * return await api.request('/data');
145
+ * }, { maxAttempts: 3, delay: 1000 });
146
+ *
147
+ * // 第1次失败 → 等待1s → 第2次失败 → 等待1s → 第3次成功 → 返回结果
148
+ * const result = await fetchData();
149
+ * ```
150
+ */
151
+ function fnRetry(fn, options = {}) {
152
+ const { maxAttempts = 3, delay = 0, retryWhen } = options;
153
+ return async function(...args) {
154
+ let lastError;
155
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) try {
156
+ return await fn.apply(this, args);
157
+ } catch (error) {
158
+ lastError = error;
159
+ if (retryWhen && !retryWhen(error)) break;
160
+ if (attempt < maxAttempts && delay > 0) await require_promise.promiseDelay(delay);
161
+ }
162
+ throw lastError;
163
+ };
164
+ }
133
165
  //#endregion
134
166
  exports.fnDebounce = fnDebounce;
135
167
  exports.fnNoop = fnNoop;
136
168
  exports.fnOnce = fnOnce;
169
+ exports.fnRetry = fnRetry;
137
170
  exports.fnThrottle = fnThrottle;
138
171
 
139
172
  //# sourceMappingURL=function.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"function.cjs","names":[],"sources":["../src/function.ts"],"sourcesContent":["import { isNumber } from './type';\nimport type { AnyFunction } from './types';\n\n/**\n * 一个空操作函数,不执行任何操作。\n *\n * @example\n * ```typescript\n * fnNoop(); // 不执行任何操作\n * ```\n */\nexport function fnNoop() {\n //\n}\n\n/**\n * 防抖函数的配置选项。\n */\nexport type DebounceOptions = {\n /**\n * 等待时间(毫秒)。\n */\n wait: number;\n /**\n * 是否在等待开始时立即执行一次\n * @default false\n */\n leading?: boolean;\n};\n\n/**\n * 创建一个防抖函数,该函数会在指定的等待时间后执行,如果在等待时间内再次调用,则重新计时。\n *\n * @param fn - 需要防抖的函数。\n * @param wait - 等待时间(毫秒)或包含 `wait` 和 `leading` 选项的对象。\n * @returns 返回一个防抖函数,该函数具有 `cancel` 方法,用于取消防抖操作。\n *\n * @example\n * ```typescript\n * const debouncedFn = fnDebounce(() => {\n * console.log('Debounced!');\n * }, 100);\n *\n * debouncedFn(); // 不会立即执行\n * debouncedFn(); // 重新计时\n * debouncedFn.cancel(); // 取消防抖操作\n * ```\n */\nexport function fnDebounce<F extends AnyFunction>(fn: F, wait: number | DebounceOptions) {\n const options: DebounceOptions = isNumber(wait) ? { wait } : wait;\n let canceled = false;\n // biome-ignore lint/suspicious/noExplicitAny: 内部使用\n let timer: any;\n let leading = false;\n\n const debounced = function (this: unknown, ...args: Parameters<F>) {\n if (canceled) return;\n\n // 第一次执行\n if (options.leading && !leading) {\n leading = true;\n fn.apply(this, args);\n return;\n }\n\n // 最后一次执行\n clearTimeout(timer);\n timer = setTimeout(() => {\n if (canceled) return;\n\n fn.apply(this, args);\n }, options.wait);\n };\n\n debounced.cancel = () => {\n clearTimeout(timer);\n canceled = true;\n };\n\n return debounced;\n}\n\nexport type ThrottleOptions = {\n /**\n * 等待时间(毫秒)。\n */\n wait: number;\n\n /**\n * 是否在第一次调用时立即执行\n * @default false\n */\n leading?: boolean;\n\n /**\n * 是否在调用结束后等待一段时间再执行\n * @default false\n */\n trailing?: boolean;\n};\n\n/**\n * 创建一个节流函数,该函数会在指定的等待时间内最多执行一次,如果在等待时间内再次调用,则忽略后续调用。\n *\n * @param fn - 需要节流的函数。\n * @param wait - 等待时间(毫秒)或包含 `wait`、`leading` 和 `trailing` 选项的对象。\n * @returns 返回一个节流函数,该函数具有 `cancel` 方法,用于取消节流操作。\n *\n * @example\n * ```typescript\n * const throttledFn = fnThrottle(() => {\n * console.log('Throttled!');\n * }, 100);\n *\n * throttledFn(); // 立即执行\n * throttledFn(); // 忽略\n * throttledFn.cancel(); // 取消节流操作\n * ```\n */\nexport function fnThrottle<F extends AnyFunction>(fn: F, wait: number | ThrottleOptions) {\n const options = isNumber(wait) ? { wait } : wait;\n const waitFinal = options.wait;\n let lastTime = 0;\n let canceled = false;\n // biome-ignore lint/suspicious/noExplicitAny: 内部使用\n let timer: any;\n\n const throttled = function (this: unknown, ...args: Parameters<F>) {\n if (canceled) return;\n\n const now = Date.now();\n let executed = false;\n\n // 第一次执行\n if (options.leading && lastTime === 0) {\n lastTime = now;\n fn.apply(this, args);\n executed = true;\n }\n\n // 中间控频执行\n else if (lastTime > 0 && now - lastTime >= waitFinal) {\n lastTime = now;\n fn.apply(this, args);\n executed = true;\n }\n\n // 首次计时\n else if (lastTime === 0) {\n lastTime = now;\n }\n\n // 最后一次执行,仅在本次未执行时才设置 trailing\n if (options.trailing && !executed) {\n clearTimeout(timer);\n timer = setTimeout(() => {\n fn.apply(this, args);\n }, waitFinal);\n }\n };\n\n throttled.cancel = () => {\n canceled = true;\n clearTimeout(timer);\n };\n\n return throttled;\n}\n\n/**\n * 创建一个只执行一次的函数,无论调用多少次,实际执行的函数体也只会执行一次。\n *\n * @param fn - 需要只执行一次的函数。\n * @returns 返回一个只执行一次的函数,该函数在第一次调用后会缓存结果并返回缓存的结果。\n *\n * @example\n * ```typescript\n * const onceFn = fnOnce(() => {\n * console.log('This will be logged only once.');\n * return 42;\n * });\n *\n * console.log(onceFn()); // 输出: This will be logged only once. 42\n * console.log(onceFn()); // 输出: 42\n * ```\n */\nexport function fnOnce<F extends AnyFunction>(fn: F) {\n let called = false;\n // biome-ignore lint/suspicious/noExplicitAny: 内部使用\n let result: any;\n\n return function (this: unknown, ...args: Parameters<F>) {\n if (!called) {\n called = true;\n result = fn.apply(this, args);\n }\n\n return result;\n };\n}\n"],"mappings":";;;;;;;;;;;AAWA,SAAgB,SAAS;;;;;;;;;;;;;;;;;;;AAqCzB,SAAgB,WAAkC,IAAO,MAAgC;CACvF,MAAM,UAA2B,aAAA,SAAS,KAAK,GAAG,EAAE,MAAM,GAAG;CAC7D,IAAI,WAAW;CAEf,IAAI;CACJ,IAAI,UAAU;CAEd,MAAM,YAAY,SAAyB,GAAG,MAAqB;EACjE,IAAI,UAAU;EAGd,IAAI,QAAQ,WAAW,CAAC,SAAS;GAC/B,UAAU;GACV,GAAG,MAAM,MAAM,KAAK;GACpB;;EAIF,aAAa,MAAM;EACnB,QAAQ,iBAAiB;GACvB,IAAI,UAAU;GAEd,GAAG,MAAM,MAAM,KAAK;KACnB,QAAQ,KAAK;;CAGlB,UAAU,eAAe;EACvB,aAAa,MAAM;EACnB,WAAW;;CAGb,OAAO;;;;;;;;;;;;;;;;;;;;AAwCT,SAAgB,WAAkC,IAAO,MAAgC;CACvF,MAAM,UAAU,aAAA,SAAS,KAAK,GAAG,EAAE,MAAM,GAAG;CAC5C,MAAM,YAAY,QAAQ;CAC1B,IAAI,WAAW;CACf,IAAI,WAAW;CAEf,IAAI;CAEJ,MAAM,YAAY,SAAyB,GAAG,MAAqB;EACjE,IAAI,UAAU;EAEd,MAAM,MAAM,KAAK,KAAK;EACtB,IAAI,WAAW;EAGf,IAAI,QAAQ,WAAW,aAAa,GAAG;GACrC,WAAW;GACX,GAAG,MAAM,MAAM,KAAK;GACpB,WAAW;SAIR,IAAI,WAAW,KAAK,MAAM,YAAY,WAAW;GACpD,WAAW;GACX,GAAG,MAAM,MAAM,KAAK;GACpB,WAAW;SAIR,IAAI,aAAa,GACpB,WAAW;EAIb,IAAI,QAAQ,YAAY,CAAC,UAAU;GACjC,aAAa,MAAM;GACnB,QAAQ,iBAAiB;IACvB,GAAG,MAAM,MAAM,KAAK;MACnB,UAAU;;;CAIjB,UAAU,eAAe;EACvB,WAAW;EACX,aAAa,MAAM;;CAGrB,OAAO;;;;;;;;;;;;;;;;;;;AAoBT,SAAgB,OAA8B,IAAO;CACnD,IAAI,SAAS;CAEb,IAAI;CAEJ,OAAO,SAAyB,GAAG,MAAqB;EACtD,IAAI,CAAC,QAAQ;GACX,SAAS;GACT,SAAS,GAAG,MAAM,MAAM,KAAK;;EAG/B,OAAO"}
1
+ {"version":3,"file":"function.cjs","names":[],"sources":["../src/function.ts"],"sourcesContent":["import { promiseDelay } from './promise';\nimport { isNumber } from './type';\nimport type { AnyFunction } from './types';\n\n/**\n * 一个空操作函数,不执行任何操作。\n *\n * @example\n * ```typescript\n * fnNoop(); // 不执行任何操作\n * ```\n */\nexport function fnNoop() {\n //\n}\n\n/**\n * 防抖函数的配置选项。\n */\nexport type DebounceOptions = {\n /**\n * 等待时间(毫秒)。\n */\n wait: number;\n /**\n * 是否在等待开始时立即执行一次\n * @default false\n */\n leading?: boolean;\n};\n\n/**\n * 创建一个防抖函数,该函数会在指定的等待时间后执行,如果在等待时间内再次调用,则重新计时。\n *\n * @param fn - 需要防抖的函数。\n * @param wait - 等待时间(毫秒)或包含 `wait` 和 `leading` 选项的对象。\n * @returns 返回一个防抖函数,该函数具有 `cancel` 方法,用于取消防抖操作。\n *\n * @example\n * ```typescript\n * const debouncedFn = fnDebounce(() => {\n * console.log('Debounced!');\n * }, 100);\n *\n * debouncedFn(); // 不会立即执行\n * debouncedFn(); // 重新计时\n * debouncedFn.cancel(); // 取消防抖操作\n * ```\n */\nexport function fnDebounce<F extends AnyFunction>(fn: F, wait: number | DebounceOptions) {\n const options: DebounceOptions = isNumber(wait) ? { wait } : wait;\n let canceled = false;\n // biome-ignore lint/suspicious/noExplicitAny: 内部使用\n let timer: any;\n let leading = false;\n\n const debounced = function (this: unknown, ...args: Parameters<F>) {\n if (canceled) return;\n\n // 第一次执行\n if (options.leading && !leading) {\n leading = true;\n fn.apply(this, args);\n return;\n }\n\n // 最后一次执行\n clearTimeout(timer);\n timer = setTimeout(() => {\n if (canceled) return;\n\n fn.apply(this, args);\n }, options.wait);\n };\n\n debounced.cancel = () => {\n clearTimeout(timer);\n canceled = true;\n };\n\n return debounced;\n}\n\nexport type ThrottleOptions = {\n /**\n * 等待时间(毫秒)。\n */\n wait: number;\n\n /**\n * 是否在第一次调用时立即执行\n * @default false\n */\n leading?: boolean;\n\n /**\n * 是否在调用结束后等待一段时间再执行\n * @default false\n */\n trailing?: boolean;\n};\n\n/**\n * 创建一个节流函数,该函数会在指定的等待时间内最多执行一次,如果在等待时间内再次调用,则忽略后续调用。\n *\n * @param fn - 需要节流的函数。\n * @param wait - 等待时间(毫秒)或包含 `wait`、`leading` 和 `trailing` 选项的对象。\n * @returns 返回一个节流函数,该函数具有 `cancel` 方法,用于取消节流操作。\n *\n * @example\n * ```typescript\n * const throttledFn = fnThrottle(() => {\n * console.log('Throttled!');\n * }, 100);\n *\n * throttledFn(); // 立即执行\n * throttledFn(); // 忽略\n * throttledFn.cancel(); // 取消节流操作\n * ```\n */\nexport function fnThrottle<F extends AnyFunction>(fn: F, wait: number | ThrottleOptions) {\n const options = isNumber(wait) ? { wait } : wait;\n const waitFinal = options.wait;\n let lastTime = 0;\n let canceled = false;\n // biome-ignore lint/suspicious/noExplicitAny: 内部使用\n let timer: any;\n\n const throttled = function (this: unknown, ...args: Parameters<F>) {\n if (canceled) return;\n\n const now = Date.now();\n let executed = false;\n\n // 第一次执行\n if (options.leading && lastTime === 0) {\n lastTime = now;\n fn.apply(this, args);\n executed = true;\n }\n\n // 中间控频执行\n else if (lastTime > 0 && now - lastTime >= waitFinal) {\n lastTime = now;\n fn.apply(this, args);\n executed = true;\n }\n\n // 首次计时\n else if (lastTime === 0) {\n lastTime = now;\n }\n\n // 最后一次执行,仅在本次未执行时才设置 trailing\n if (options.trailing && !executed) {\n clearTimeout(timer);\n timer = setTimeout(() => {\n fn.apply(this, args);\n }, waitFinal);\n }\n };\n\n throttled.cancel = () => {\n canceled = true;\n clearTimeout(timer);\n };\n\n return throttled;\n}\n\n/**\n * 创建一个只执行一次的函数,无论调用多少次,实际执行的函数体也只会执行一次。\n *\n * @param fn - 需要只执行一次的函数。\n * @returns 返回一个只执行一次的函数,该函数在第一次调用后会缓存结果并返回缓存的结果。\n *\n * @example\n * ```typescript\n * const onceFn = fnOnce(() => {\n * console.log('This will be logged only once.');\n * return 42;\n * });\n *\n * console.log(onceFn()); // 输出: This will be logged only once. 42\n * console.log(onceFn()); // 输出: 42\n * ```\n */\nexport function fnOnce<F extends AnyFunction>(fn: F) {\n let called = false;\n // biome-ignore lint/suspicious/noExplicitAny: 内部使用\n let result: any;\n\n return function (this: unknown, ...args: Parameters<F>) {\n if (!called) {\n called = true;\n result = fn.apply(this, args);\n }\n\n return result;\n };\n}\n\nexport type FnRetryOptions = {\n /**\n * 最大尝试次数\n */\n maxAttempts?: number;\n /**\n * 每次重试之间的延迟时间(毫秒)\n * @default 0\n */\n delay?: number;\n /**\n * 自定义重试条件,返回 true 时触发重试\n * @default 仅在抛出错误时重试\n */\n retryWhen?: (error: unknown) => boolean;\n};\n\n/**\n * 创建一个带重试能力的函数,失败时自动重试直到成功或耗尽次数。\n *\n * @param fn - 需要重试的函数。\n * @param options - 重试配置选项。\n * @returns 返回一个带重试能力的函数。\n *\n * @example\n * ```typescript\n * const fetchData = fnRetry(async () => {\n * return await api.request('/data');\n * }, { maxAttempts: 3, delay: 1000 });\n *\n * // 第1次失败 → 等待1s → 第2次失败 → 等待1s → 第3次成功 → 返回结果\n * const result = await fetchData();\n * ```\n */\nexport function fnRetry<F extends AnyFunction>(fn: F, options: FnRetryOptions = {}) {\n const { maxAttempts = 3, delay = 0, retryWhen } = options;\n\n return async function (this: unknown, ...args: Parameters<F>): Promise<Awaited<ReturnType<F>>> {\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return (await fn.apply(this, args)) as Awaited<ReturnType<F>>;\n } catch (error) {\n lastError = error;\n\n if (retryWhen && !retryWhen(error)) break;\n\n if (attempt < maxAttempts && delay > 0) {\n await promiseDelay(delay);\n }\n }\n }\n\n throw lastError;\n };\n}\n"],"mappings":";;;;;;;;;;;;AAYA,SAAgB,SAAS;;;;;;;;;;;;;;;;;;;AAqCzB,SAAgB,WAAkC,IAAO,MAAgC;CACvF,MAAM,UAA2B,aAAA,SAAS,KAAK,GAAG,EAAE,MAAM,GAAG;CAC7D,IAAI,WAAW;CAEf,IAAI;CACJ,IAAI,UAAU;CAEd,MAAM,YAAY,SAAyB,GAAG,MAAqB;EACjE,IAAI,UAAU;EAGd,IAAI,QAAQ,WAAW,CAAC,SAAS;GAC/B,UAAU;GACV,GAAG,MAAM,MAAM,KAAK;GACpB;;EAIF,aAAa,MAAM;EACnB,QAAQ,iBAAiB;GACvB,IAAI,UAAU;GAEd,GAAG,MAAM,MAAM,KAAK;KACnB,QAAQ,KAAK;;CAGlB,UAAU,eAAe;EACvB,aAAa,MAAM;EACnB,WAAW;;CAGb,OAAO;;;;;;;;;;;;;;;;;;;;AAwCT,SAAgB,WAAkC,IAAO,MAAgC;CACvF,MAAM,UAAU,aAAA,SAAS,KAAK,GAAG,EAAE,MAAM,GAAG;CAC5C,MAAM,YAAY,QAAQ;CAC1B,IAAI,WAAW;CACf,IAAI,WAAW;CAEf,IAAI;CAEJ,MAAM,YAAY,SAAyB,GAAG,MAAqB;EACjE,IAAI,UAAU;EAEd,MAAM,MAAM,KAAK,KAAK;EACtB,IAAI,WAAW;EAGf,IAAI,QAAQ,WAAW,aAAa,GAAG;GACrC,WAAW;GACX,GAAG,MAAM,MAAM,KAAK;GACpB,WAAW;SAIR,IAAI,WAAW,KAAK,MAAM,YAAY,WAAW;GACpD,WAAW;GACX,GAAG,MAAM,MAAM,KAAK;GACpB,WAAW;SAIR,IAAI,aAAa,GACpB,WAAW;EAIb,IAAI,QAAQ,YAAY,CAAC,UAAU;GACjC,aAAa,MAAM;GACnB,QAAQ,iBAAiB;IACvB,GAAG,MAAM,MAAM,KAAK;MACnB,UAAU;;;CAIjB,UAAU,eAAe;EACvB,WAAW;EACX,aAAa,MAAM;;CAGrB,OAAO;;;;;;;;;;;;;;;;;;;AAoBT,SAAgB,OAA8B,IAAO;CACnD,IAAI,SAAS;CAEb,IAAI;CAEJ,OAAO,SAAyB,GAAG,MAAqB;EACtD,IAAI,CAAC,QAAQ;GACX,SAAS;GACT,SAAS,GAAG,MAAM,MAAM,KAAK;;EAG/B,OAAO;;;;;;;;;;;;;;;;;;;;AAsCX,SAAgB,QAA+B,IAAO,UAA0B,EAAE,EAAE;CAClF,MAAM,EAAE,cAAc,GAAG,QAAQ,GAAG,cAAc;CAElD,OAAO,eAA+B,GAAG,MAAsD;EAC7F,IAAI;EAEJ,KAAK,IAAI,UAAU,GAAG,WAAW,aAAa,WAC5C,IAAI;GACF,OAAQ,MAAM,GAAG,MAAM,MAAM,KAAK;WAC3B,OAAO;GACd,YAAY;GAEZ,IAAI,aAAa,CAAC,UAAU,MAAM,EAAE;GAEpC,IAAI,UAAU,eAAe,QAAQ,GACnC,MAAM,gBAAA,aAAa,MAAM;;EAK/B,MAAM"}
@@ -100,3 +100,37 @@ export declare function fnThrottle<F extends AnyFunction>(fn: F, wait: number |
100
100
  * ```
101
101
  */
102
102
  export declare function fnOnce<F extends AnyFunction>(fn: F): (this: unknown, ...args: Parameters<F>) => any;
103
+ export type FnRetryOptions = {
104
+ /**
105
+ * 最大尝试次数
106
+ */
107
+ maxAttempts?: number;
108
+ /**
109
+ * 每次重试之间的延迟时间(毫秒)
110
+ * @default 0
111
+ */
112
+ delay?: number;
113
+ /**
114
+ * 自定义重试条件,返回 true 时触发重试
115
+ * @default 仅在抛出错误时重试
116
+ */
117
+ retryWhen?: (error: unknown) => boolean;
118
+ };
119
+ /**
120
+ * 创建一个带重试能力的函数,失败时自动重试直到成功或耗尽次数。
121
+ *
122
+ * @param fn - 需要重试的函数。
123
+ * @param options - 重试配置选项。
124
+ * @returns 返回一个带重试能力的函数。
125
+ *
126
+ * @example
127
+ * ```typescript
128
+ * const fetchData = fnRetry(async () => {
129
+ * return await api.request('/data');
130
+ * }, { maxAttempts: 3, delay: 1000 });
131
+ *
132
+ * // 第1次失败 → 等待1s → 第2次失败 → 等待1s → 第3次成功 → 返回结果
133
+ * const result = await fetchData();
134
+ * ```
135
+ */
136
+ export declare function fnRetry<F extends AnyFunction>(fn: F, options?: FnRetryOptions): (this: unknown, ...args: Parameters<F>) => Promise<Awaited<ReturnType<F>>>;
package/dist/function.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import { isNumber } from "./type.mjs";
2
+ import { promiseDelay } from "./promise.mjs";
2
3
  //#region src/function.ts
3
4
  /**
4
5
  * 一个空操作函数,不执行任何操作。
@@ -129,7 +130,38 @@ function fnOnce(fn) {
129
130
  return result;
130
131
  };
131
132
  }
133
+ /**
134
+ * 创建一个带重试能力的函数,失败时自动重试直到成功或耗尽次数。
135
+ *
136
+ * @param fn - 需要重试的函数。
137
+ * @param options - 重试配置选项。
138
+ * @returns 返回一个带重试能力的函数。
139
+ *
140
+ * @example
141
+ * ```typescript
142
+ * const fetchData = fnRetry(async () => {
143
+ * return await api.request('/data');
144
+ * }, { maxAttempts: 3, delay: 1000 });
145
+ *
146
+ * // 第1次失败 → 等待1s → 第2次失败 → 等待1s → 第3次成功 → 返回结果
147
+ * const result = await fetchData();
148
+ * ```
149
+ */
150
+ function fnRetry(fn, options = {}) {
151
+ const { maxAttempts = 3, delay = 0, retryWhen } = options;
152
+ return async function(...args) {
153
+ let lastError;
154
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) try {
155
+ return await fn.apply(this, args);
156
+ } catch (error) {
157
+ lastError = error;
158
+ if (retryWhen && !retryWhen(error)) break;
159
+ if (attempt < maxAttempts && delay > 0) await promiseDelay(delay);
160
+ }
161
+ throw lastError;
162
+ };
163
+ }
132
164
  //#endregion
133
- export { fnDebounce, fnNoop, fnOnce, fnThrottle };
165
+ export { fnDebounce, fnNoop, fnOnce, fnRetry, fnThrottle };
134
166
 
135
167
  //# sourceMappingURL=function.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"function.mjs","names":[],"sources":["../src/function.ts"],"sourcesContent":["import { isNumber } from './type';\nimport type { AnyFunction } from './types';\n\n/**\n * 一个空操作函数,不执行任何操作。\n *\n * @example\n * ```typescript\n * fnNoop(); // 不执行任何操作\n * ```\n */\nexport function fnNoop() {\n //\n}\n\n/**\n * 防抖函数的配置选项。\n */\nexport type DebounceOptions = {\n /**\n * 等待时间(毫秒)。\n */\n wait: number;\n /**\n * 是否在等待开始时立即执行一次\n * @default false\n */\n leading?: boolean;\n};\n\n/**\n * 创建一个防抖函数,该函数会在指定的等待时间后执行,如果在等待时间内再次调用,则重新计时。\n *\n * @param fn - 需要防抖的函数。\n * @param wait - 等待时间(毫秒)或包含 `wait` 和 `leading` 选项的对象。\n * @returns 返回一个防抖函数,该函数具有 `cancel` 方法,用于取消防抖操作。\n *\n * @example\n * ```typescript\n * const debouncedFn = fnDebounce(() => {\n * console.log('Debounced!');\n * }, 100);\n *\n * debouncedFn(); // 不会立即执行\n * debouncedFn(); // 重新计时\n * debouncedFn.cancel(); // 取消防抖操作\n * ```\n */\nexport function fnDebounce<F extends AnyFunction>(fn: F, wait: number | DebounceOptions) {\n const options: DebounceOptions = isNumber(wait) ? { wait } : wait;\n let canceled = false;\n // biome-ignore lint/suspicious/noExplicitAny: 内部使用\n let timer: any;\n let leading = false;\n\n const debounced = function (this: unknown, ...args: Parameters<F>) {\n if (canceled) return;\n\n // 第一次执行\n if (options.leading && !leading) {\n leading = true;\n fn.apply(this, args);\n return;\n }\n\n // 最后一次执行\n clearTimeout(timer);\n timer = setTimeout(() => {\n if (canceled) return;\n\n fn.apply(this, args);\n }, options.wait);\n };\n\n debounced.cancel = () => {\n clearTimeout(timer);\n canceled = true;\n };\n\n return debounced;\n}\n\nexport type ThrottleOptions = {\n /**\n * 等待时间(毫秒)。\n */\n wait: number;\n\n /**\n * 是否在第一次调用时立即执行\n * @default false\n */\n leading?: boolean;\n\n /**\n * 是否在调用结束后等待一段时间再执行\n * @default false\n */\n trailing?: boolean;\n};\n\n/**\n * 创建一个节流函数,该函数会在指定的等待时间内最多执行一次,如果在等待时间内再次调用,则忽略后续调用。\n *\n * @param fn - 需要节流的函数。\n * @param wait - 等待时间(毫秒)或包含 `wait`、`leading` 和 `trailing` 选项的对象。\n * @returns 返回一个节流函数,该函数具有 `cancel` 方法,用于取消节流操作。\n *\n * @example\n * ```typescript\n * const throttledFn = fnThrottle(() => {\n * console.log('Throttled!');\n * }, 100);\n *\n * throttledFn(); // 立即执行\n * throttledFn(); // 忽略\n * throttledFn.cancel(); // 取消节流操作\n * ```\n */\nexport function fnThrottle<F extends AnyFunction>(fn: F, wait: number | ThrottleOptions) {\n const options = isNumber(wait) ? { wait } : wait;\n const waitFinal = options.wait;\n let lastTime = 0;\n let canceled = false;\n // biome-ignore lint/suspicious/noExplicitAny: 内部使用\n let timer: any;\n\n const throttled = function (this: unknown, ...args: Parameters<F>) {\n if (canceled) return;\n\n const now = Date.now();\n let executed = false;\n\n // 第一次执行\n if (options.leading && lastTime === 0) {\n lastTime = now;\n fn.apply(this, args);\n executed = true;\n }\n\n // 中间控频执行\n else if (lastTime > 0 && now - lastTime >= waitFinal) {\n lastTime = now;\n fn.apply(this, args);\n executed = true;\n }\n\n // 首次计时\n else if (lastTime === 0) {\n lastTime = now;\n }\n\n // 最后一次执行,仅在本次未执行时才设置 trailing\n if (options.trailing && !executed) {\n clearTimeout(timer);\n timer = setTimeout(() => {\n fn.apply(this, args);\n }, waitFinal);\n }\n };\n\n throttled.cancel = () => {\n canceled = true;\n clearTimeout(timer);\n };\n\n return throttled;\n}\n\n/**\n * 创建一个只执行一次的函数,无论调用多少次,实际执行的函数体也只会执行一次。\n *\n * @param fn - 需要只执行一次的函数。\n * @returns 返回一个只执行一次的函数,该函数在第一次调用后会缓存结果并返回缓存的结果。\n *\n * @example\n * ```typescript\n * const onceFn = fnOnce(() => {\n * console.log('This will be logged only once.');\n * return 42;\n * });\n *\n * console.log(onceFn()); // 输出: This will be logged only once. 42\n * console.log(onceFn()); // 输出: 42\n * ```\n */\nexport function fnOnce<F extends AnyFunction>(fn: F) {\n let called = false;\n // biome-ignore lint/suspicious/noExplicitAny: 内部使用\n let result: any;\n\n return function (this: unknown, ...args: Parameters<F>) {\n if (!called) {\n called = true;\n result = fn.apply(this, args);\n }\n\n return result;\n };\n}\n"],"mappings":";;;;;;;;;;AAWA,SAAgB,SAAS;;;;;;;;;;;;;;;;;;;AAqCzB,SAAgB,WAAkC,IAAO,MAAgC;CACvF,MAAM,UAA2B,SAAS,KAAK,GAAG,EAAE,MAAM,GAAG;CAC7D,IAAI,WAAW;CAEf,IAAI;CACJ,IAAI,UAAU;CAEd,MAAM,YAAY,SAAyB,GAAG,MAAqB;EACjE,IAAI,UAAU;EAGd,IAAI,QAAQ,WAAW,CAAC,SAAS;GAC/B,UAAU;GACV,GAAG,MAAM,MAAM,KAAK;GACpB;;EAIF,aAAa,MAAM;EACnB,QAAQ,iBAAiB;GACvB,IAAI,UAAU;GAEd,GAAG,MAAM,MAAM,KAAK;KACnB,QAAQ,KAAK;;CAGlB,UAAU,eAAe;EACvB,aAAa,MAAM;EACnB,WAAW;;CAGb,OAAO;;;;;;;;;;;;;;;;;;;;AAwCT,SAAgB,WAAkC,IAAO,MAAgC;CACvF,MAAM,UAAU,SAAS,KAAK,GAAG,EAAE,MAAM,GAAG;CAC5C,MAAM,YAAY,QAAQ;CAC1B,IAAI,WAAW;CACf,IAAI,WAAW;CAEf,IAAI;CAEJ,MAAM,YAAY,SAAyB,GAAG,MAAqB;EACjE,IAAI,UAAU;EAEd,MAAM,MAAM,KAAK,KAAK;EACtB,IAAI,WAAW;EAGf,IAAI,QAAQ,WAAW,aAAa,GAAG;GACrC,WAAW;GACX,GAAG,MAAM,MAAM,KAAK;GACpB,WAAW;SAIR,IAAI,WAAW,KAAK,MAAM,YAAY,WAAW;GACpD,WAAW;GACX,GAAG,MAAM,MAAM,KAAK;GACpB,WAAW;SAIR,IAAI,aAAa,GACpB,WAAW;EAIb,IAAI,QAAQ,YAAY,CAAC,UAAU;GACjC,aAAa,MAAM;GACnB,QAAQ,iBAAiB;IACvB,GAAG,MAAM,MAAM,KAAK;MACnB,UAAU;;;CAIjB,UAAU,eAAe;EACvB,WAAW;EACX,aAAa,MAAM;;CAGrB,OAAO;;;;;;;;;;;;;;;;;;;AAoBT,SAAgB,OAA8B,IAAO;CACnD,IAAI,SAAS;CAEb,IAAI;CAEJ,OAAO,SAAyB,GAAG,MAAqB;EACtD,IAAI,CAAC,QAAQ;GACX,SAAS;GACT,SAAS,GAAG,MAAM,MAAM,KAAK;;EAG/B,OAAO"}
1
+ {"version":3,"file":"function.mjs","names":[],"sources":["../src/function.ts"],"sourcesContent":["import { promiseDelay } from './promise';\nimport { isNumber } from './type';\nimport type { AnyFunction } from './types';\n\n/**\n * 一个空操作函数,不执行任何操作。\n *\n * @example\n * ```typescript\n * fnNoop(); // 不执行任何操作\n * ```\n */\nexport function fnNoop() {\n //\n}\n\n/**\n * 防抖函数的配置选项。\n */\nexport type DebounceOptions = {\n /**\n * 等待时间(毫秒)。\n */\n wait: number;\n /**\n * 是否在等待开始时立即执行一次\n * @default false\n */\n leading?: boolean;\n};\n\n/**\n * 创建一个防抖函数,该函数会在指定的等待时间后执行,如果在等待时间内再次调用,则重新计时。\n *\n * @param fn - 需要防抖的函数。\n * @param wait - 等待时间(毫秒)或包含 `wait` 和 `leading` 选项的对象。\n * @returns 返回一个防抖函数,该函数具有 `cancel` 方法,用于取消防抖操作。\n *\n * @example\n * ```typescript\n * const debouncedFn = fnDebounce(() => {\n * console.log('Debounced!');\n * }, 100);\n *\n * debouncedFn(); // 不会立即执行\n * debouncedFn(); // 重新计时\n * debouncedFn.cancel(); // 取消防抖操作\n * ```\n */\nexport function fnDebounce<F extends AnyFunction>(fn: F, wait: number | DebounceOptions) {\n const options: DebounceOptions = isNumber(wait) ? { wait } : wait;\n let canceled = false;\n // biome-ignore lint/suspicious/noExplicitAny: 内部使用\n let timer: any;\n let leading = false;\n\n const debounced = function (this: unknown, ...args: Parameters<F>) {\n if (canceled) return;\n\n // 第一次执行\n if (options.leading && !leading) {\n leading = true;\n fn.apply(this, args);\n return;\n }\n\n // 最后一次执行\n clearTimeout(timer);\n timer = setTimeout(() => {\n if (canceled) return;\n\n fn.apply(this, args);\n }, options.wait);\n };\n\n debounced.cancel = () => {\n clearTimeout(timer);\n canceled = true;\n };\n\n return debounced;\n}\n\nexport type ThrottleOptions = {\n /**\n * 等待时间(毫秒)。\n */\n wait: number;\n\n /**\n * 是否在第一次调用时立即执行\n * @default false\n */\n leading?: boolean;\n\n /**\n * 是否在调用结束后等待一段时间再执行\n * @default false\n */\n trailing?: boolean;\n};\n\n/**\n * 创建一个节流函数,该函数会在指定的等待时间内最多执行一次,如果在等待时间内再次调用,则忽略后续调用。\n *\n * @param fn - 需要节流的函数。\n * @param wait - 等待时间(毫秒)或包含 `wait`、`leading` 和 `trailing` 选项的对象。\n * @returns 返回一个节流函数,该函数具有 `cancel` 方法,用于取消节流操作。\n *\n * @example\n * ```typescript\n * const throttledFn = fnThrottle(() => {\n * console.log('Throttled!');\n * }, 100);\n *\n * throttledFn(); // 立即执行\n * throttledFn(); // 忽略\n * throttledFn.cancel(); // 取消节流操作\n * ```\n */\nexport function fnThrottle<F extends AnyFunction>(fn: F, wait: number | ThrottleOptions) {\n const options = isNumber(wait) ? { wait } : wait;\n const waitFinal = options.wait;\n let lastTime = 0;\n let canceled = false;\n // biome-ignore lint/suspicious/noExplicitAny: 内部使用\n let timer: any;\n\n const throttled = function (this: unknown, ...args: Parameters<F>) {\n if (canceled) return;\n\n const now = Date.now();\n let executed = false;\n\n // 第一次执行\n if (options.leading && lastTime === 0) {\n lastTime = now;\n fn.apply(this, args);\n executed = true;\n }\n\n // 中间控频执行\n else if (lastTime > 0 && now - lastTime >= waitFinal) {\n lastTime = now;\n fn.apply(this, args);\n executed = true;\n }\n\n // 首次计时\n else if (lastTime === 0) {\n lastTime = now;\n }\n\n // 最后一次执行,仅在本次未执行时才设置 trailing\n if (options.trailing && !executed) {\n clearTimeout(timer);\n timer = setTimeout(() => {\n fn.apply(this, args);\n }, waitFinal);\n }\n };\n\n throttled.cancel = () => {\n canceled = true;\n clearTimeout(timer);\n };\n\n return throttled;\n}\n\n/**\n * 创建一个只执行一次的函数,无论调用多少次,实际执行的函数体也只会执行一次。\n *\n * @param fn - 需要只执行一次的函数。\n * @returns 返回一个只执行一次的函数,该函数在第一次调用后会缓存结果并返回缓存的结果。\n *\n * @example\n * ```typescript\n * const onceFn = fnOnce(() => {\n * console.log('This will be logged only once.');\n * return 42;\n * });\n *\n * console.log(onceFn()); // 输出: This will be logged only once. 42\n * console.log(onceFn()); // 输出: 42\n * ```\n */\nexport function fnOnce<F extends AnyFunction>(fn: F) {\n let called = false;\n // biome-ignore lint/suspicious/noExplicitAny: 内部使用\n let result: any;\n\n return function (this: unknown, ...args: Parameters<F>) {\n if (!called) {\n called = true;\n result = fn.apply(this, args);\n }\n\n return result;\n };\n}\n\nexport type FnRetryOptions = {\n /**\n * 最大尝试次数\n */\n maxAttempts?: number;\n /**\n * 每次重试之间的延迟时间(毫秒)\n * @default 0\n */\n delay?: number;\n /**\n * 自定义重试条件,返回 true 时触发重试\n * @default 仅在抛出错误时重试\n */\n retryWhen?: (error: unknown) => boolean;\n};\n\n/**\n * 创建一个带重试能力的函数,失败时自动重试直到成功或耗尽次数。\n *\n * @param fn - 需要重试的函数。\n * @param options - 重试配置选项。\n * @returns 返回一个带重试能力的函数。\n *\n * @example\n * ```typescript\n * const fetchData = fnRetry(async () => {\n * return await api.request('/data');\n * }, { maxAttempts: 3, delay: 1000 });\n *\n * // 第1次失败 → 等待1s → 第2次失败 → 等待1s → 第3次成功 → 返回结果\n * const result = await fetchData();\n * ```\n */\nexport function fnRetry<F extends AnyFunction>(fn: F, options: FnRetryOptions = {}) {\n const { maxAttempts = 3, delay = 0, retryWhen } = options;\n\n return async function (this: unknown, ...args: Parameters<F>): Promise<Awaited<ReturnType<F>>> {\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return (await fn.apply(this, args)) as Awaited<ReturnType<F>>;\n } catch (error) {\n lastError = error;\n\n if (retryWhen && !retryWhen(error)) break;\n\n if (attempt < maxAttempts && delay > 0) {\n await promiseDelay(delay);\n }\n }\n }\n\n throw lastError;\n };\n}\n"],"mappings":";;;;;;;;;;;AAYA,SAAgB,SAAS;;;;;;;;;;;;;;;;;;;AAqCzB,SAAgB,WAAkC,IAAO,MAAgC;CACvF,MAAM,UAA2B,SAAS,KAAK,GAAG,EAAE,MAAM,GAAG;CAC7D,IAAI,WAAW;CAEf,IAAI;CACJ,IAAI,UAAU;CAEd,MAAM,YAAY,SAAyB,GAAG,MAAqB;EACjE,IAAI,UAAU;EAGd,IAAI,QAAQ,WAAW,CAAC,SAAS;GAC/B,UAAU;GACV,GAAG,MAAM,MAAM,KAAK;GACpB;;EAIF,aAAa,MAAM;EACnB,QAAQ,iBAAiB;GACvB,IAAI,UAAU;GAEd,GAAG,MAAM,MAAM,KAAK;KACnB,QAAQ,KAAK;;CAGlB,UAAU,eAAe;EACvB,aAAa,MAAM;EACnB,WAAW;;CAGb,OAAO;;;;;;;;;;;;;;;;;;;;AAwCT,SAAgB,WAAkC,IAAO,MAAgC;CACvF,MAAM,UAAU,SAAS,KAAK,GAAG,EAAE,MAAM,GAAG;CAC5C,MAAM,YAAY,QAAQ;CAC1B,IAAI,WAAW;CACf,IAAI,WAAW;CAEf,IAAI;CAEJ,MAAM,YAAY,SAAyB,GAAG,MAAqB;EACjE,IAAI,UAAU;EAEd,MAAM,MAAM,KAAK,KAAK;EACtB,IAAI,WAAW;EAGf,IAAI,QAAQ,WAAW,aAAa,GAAG;GACrC,WAAW;GACX,GAAG,MAAM,MAAM,KAAK;GACpB,WAAW;SAIR,IAAI,WAAW,KAAK,MAAM,YAAY,WAAW;GACpD,WAAW;GACX,GAAG,MAAM,MAAM,KAAK;GACpB,WAAW;SAIR,IAAI,aAAa,GACpB,WAAW;EAIb,IAAI,QAAQ,YAAY,CAAC,UAAU;GACjC,aAAa,MAAM;GACnB,QAAQ,iBAAiB;IACvB,GAAG,MAAM,MAAM,KAAK;MACnB,UAAU;;;CAIjB,UAAU,eAAe;EACvB,WAAW;EACX,aAAa,MAAM;;CAGrB,OAAO;;;;;;;;;;;;;;;;;;;AAoBT,SAAgB,OAA8B,IAAO;CACnD,IAAI,SAAS;CAEb,IAAI;CAEJ,OAAO,SAAyB,GAAG,MAAqB;EACtD,IAAI,CAAC,QAAQ;GACX,SAAS;GACT,SAAS,GAAG,MAAM,MAAM,KAAK;;EAG/B,OAAO;;;;;;;;;;;;;;;;;;;;AAsCX,SAAgB,QAA+B,IAAO,UAA0B,EAAE,EAAE;CAClF,MAAM,EAAE,cAAc,GAAG,QAAQ,GAAG,cAAc;CAElD,OAAO,eAA+B,GAAG,MAAsD;EAC7F,IAAI;EAEJ,KAAK,IAAI,UAAU,GAAG,WAAW,aAAa,WAC5C,IAAI;GACF,OAAQ,MAAM,GAAG,MAAM,MAAM,KAAK;WAC3B,OAAO;GACd,YAAY;GAEZ,IAAI,aAAa,CAAC,UAAU,MAAM,EAAE;GAEpC,IAAI,UAAU,eAAe,QAAQ,GACnC,MAAM,aAAa,MAAM;;EAK/B,MAAM"}
package/dist/index.cjs CHANGED
@@ -3,7 +3,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  /**
4
4
  * `@cloudcome/utils-core` 版本号
5
5
  */
6
- var VERSION = "1.20.1";
6
+ var VERSION = "1.22.0";
7
7
  //#endregion
8
8
  exports.VERSION = VERSION;
9
9
 
package/dist/index.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  /**
3
3
  * `@cloudcome/utils-core` 版本号
4
4
  */
5
- var VERSION = "1.20.1";
5
+ var VERSION = "1.22.0";
6
6
  //#endregion
7
7
  export { VERSION };
8
8
 
package/dist/timer.cjs CHANGED
@@ -5,13 +5,29 @@ var STATUS_START = 1;
5
5
  var STATUS_PAUSE = 2;
6
6
  var STATUS_STOP = 3;
7
7
  /**
8
- * 创建间隔定时器核心函数
8
+ * 创建可控制的间隔定时器核心函数
9
9
  *
10
- * @param nextTime - 用于安排下一次执行的函数
11
- * @param effect - 每次执行的回调函数,接收定时器状态和可选的next函数
12
- * @returns 返回包含控制方法的对象
10
+ * @example
11
+ * ```typescript
12
+ * // 无 condition,state.data 为 null
13
+ * makeInterval({
14
+ * dispatcher: (dispatch) => setTimeout(dispatch, 1000),
15
+ * runner: (state) => console.log(state.times),
16
+ * })
17
+ *
18
+ * // 有 condition,T 自动推断为 number
19
+ * makeInterval({
20
+ * dispatcher: (dispatch) => setTimeout(dispatch, 1000),
21
+ * condition: (state) => state.times,
22
+ * runner: (state) => state.data.toFixed(2),
23
+ * })
24
+ * ```
25
+ *
26
+ * @param options - 配置选项
27
+ * @returns 定时器控制方法集合
13
28
  */
14
- function makeInterval(nextTime, effect) {
29
+ function makeInterval(options) {
30
+ const { dispatcher, runner, condition, leading, trailing } = options;
15
31
  let startAt = 0;
16
32
  let lastAt = 0;
17
33
  let stopAt = 0;
@@ -20,14 +36,14 @@ function makeInterval(nextTime, effect) {
20
36
  let times = 0;
21
37
  let status = STATUS_READY;
22
38
  let runningTime = 0;
23
- const execute = () => {
39
+ const execute = async () => {
24
40
  if (status >= STATUS_PAUSE) return;
25
41
  const now = Date.now();
26
42
  const intervalTime = lastAt > 0 ? now - lastAt : 0;
27
43
  runningTime += intervalTime;
28
44
  lastAt = now;
29
45
  const state = {
30
- times: ++times,
46
+ times,
31
47
  startAt,
32
48
  stopAt,
33
49
  pauseAt,
@@ -35,32 +51,38 @@ function makeInterval(nextTime, effect) {
35
51
  currentAt: now,
36
52
  elapsedTime: startAt > 0 ? now - startAt : 0,
37
53
  runningTime,
38
- intervalTime
54
+ intervalTime,
55
+ data: null
39
56
  };
40
- if (effect.length === 2) effect(state, () => {
41
- nextTime(execute);
42
- });
43
- else {
44
- effect(state);
45
- nextTime(execute);
57
+ if (condition) try {
58
+ state.data = await condition(state);
59
+ } catch {
60
+ dispatcher(execute);
61
+ return;
46
62
  }
63
+ state.times = ++times;
64
+ await runner(state);
65
+ dispatcher(execute);
47
66
  };
48
67
  const canStart = () => status === STATUS_READY;
49
68
  const start = () => {
50
69
  if (!canStart()) return;
51
70
  status = STATUS_START;
52
71
  startAt = Date.now();
53
- execute();
72
+ if (leading === false) dispatcher(execute);
73
+ else execute();
54
74
  };
55
75
  const canStop = () => status === STATUS_START;
56
76
  const stop = () => {
57
77
  if (!canStop()) return;
78
+ if (trailing) execute();
58
79
  status = STATUS_STOP;
59
80
  stopAt = Date.now();
60
81
  };
61
82
  const canPause = () => status === STATUS_START;
62
83
  const pause = () => {
63
84
  if (!canPause()) return;
85
+ if (trailing) execute();
64
86
  status = STATUS_PAUSE;
65
87
  pauseAt = Date.now();
66
88
  };
@@ -85,45 +107,67 @@ function makeInterval(nextTime, effect) {
85
107
  };
86
108
  }
87
109
  /**
88
- * 创建一个基于 `setTimeout` 的间隔定时器
110
+ * 创建基于 setTimeout 的间隔定时器
111
+ *
112
+ * @example
113
+ * ```typescript
114
+ * // 无 condition
115
+ * timerInterval({
116
+ * interval: 1000,
117
+ * runner: (state) => console.log(state.times),
118
+ * })
119
+ *
120
+ * // 有 condition,T 自动推断为 number
121
+ * timerInterval({
122
+ * interval: 1000,
123
+ * condition: (state) => state.times,
124
+ * runner: (state) => state.data.toFixed(2),
125
+ * })
126
+ * ```
89
127
  *
90
- * @param callback - 每次间隔执行的回调函数,接收定时器状态和可选的 `next` 函数
91
- * @param interval - 间隔时间,单位为毫秒
92
128
  * @param options - 配置选项
93
- * @returns {TimerHandler}
129
+ * @returns 定时器控制方法集合
94
130
  */
95
- function timeInterval(callback, interval, options) {
131
+ function timerInterval(options) {
132
+ const { runner, interval, condition, leading, trailing } = options;
96
133
  let timeId;
97
- const { canStart, canStop, canPause, canResume, start, stop, pause, resume, execute } = makeInterval((call) => {
98
- timeId = setTimeout(call, interval);
99
- }, callback);
134
+ const { canStart, canStop, canPause, canResume, start, stop, pause, resume, execute } = makeInterval({
135
+ dispatcher: (dispatch) => {
136
+ timeId = setTimeout(dispatch, interval);
137
+ },
138
+ runner,
139
+ condition,
140
+ leading: leading ?? false,
141
+ trailing
142
+ });
100
143
  return {
101
144
  start() {
102
145
  if (!canStart()) return;
103
- if (options?.leading) start();
104
- else timeId = setTimeout(start, interval);
146
+ start();
105
147
  },
106
148
  stop() {
107
149
  if (!canStop()) return;
108
- if (options?.trailing) execute();
109
- clearTimeout(timeId);
110
150
  stop();
151
+ clearTimeout(timeId);
111
152
  },
112
153
  pause() {
113
154
  if (!canPause()) return;
114
- if (options?.trailing) execute();
115
- clearTimeout(timeId);
116
155
  pause();
156
+ clearTimeout(timeId);
117
157
  },
118
158
  resume(immediate) {
119
159
  if (!canResume()) return;
120
- if (immediate || options?.leading) resume();
121
- else timeId = setTimeout(resume, interval);
160
+ if (immediate || leading) resume();
161
+ else timeId = setTimeout(() => resume(), interval);
162
+ },
163
+ execute() {
164
+ clearTimeout(timeId);
165
+ execute();
122
166
  }
123
167
  };
124
168
  }
125
169
  //#endregion
126
170
  exports.makeInterval = makeInterval;
127
- exports.timeInterval = timeInterval;
171
+ exports.timerInterval = timerInterval;
128
172
 
129
173
  //# sourceMappingURL=timer.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"timer.cjs","names":[],"sources":["../src/timer.ts"],"sourcesContent":["/**\n * 定时器状态接口\n */\nexport type TimerState = {\n /**\n * 执行次数\n */\n times: number;\n /**\n * 开始时间戳\n */\n startAt: number;\n /**\n * 停止时间戳\n */\n stopAt: number;\n /**\n * 暂停时间戳\n */\n pauseAt: number;\n /**\n * 恢复时间戳\n */\n resumeAt: number;\n /**\n * 当前时间戳\n */\n currentAt: number;\n /**\n * 总耗时(包括暂停时间)\n */\n elapsedTime: number;\n /**\n * 实际运行时间(不包括暂停时间)\n */\n runningTime: number;\n /**\n * 当前间隔时间\n */\n intervalTime: number;\n};\n\nexport type TimerHandler = {\n /**\n * 开始\n */\n start: () => void;\n /**\n * 暂停\n */\n pause: () => void;\n /**\n * 恢复\n */\n resume: (immediate?: boolean) => void;\n /**\n * 停止\n */\n stop: () => void;\n};\n\nconst STATUS_READY = 0;\nconst STATUS_START = 1;\nconst STATUS_PAUSE = 2;\nconst STATUS_STOP = 3;\n\n/**\n * 创建间隔定时器核心函数\n *\n * @param nextTime - 用于安排下一次执行的函数\n * @param effect - 每次执行的回调函数,接收定时器状态和可选的next函数\n * @returns 返回包含控制方法的对象\n */\nexport function makeInterval(\n nextTime: (call: () => void) => void,\n effect: (timer: TimerState, next?: () => void) => unknown,\n) {\n let startAt = 0;\n let lastAt = 0;\n let stopAt = 0;\n let pauseAt = 0;\n let resumeAt = 0;\n let times = 0;\n let status = STATUS_READY;\n let runningTime = 0;\n\n const execute = () => {\n if (status >= STATUS_PAUSE) return;\n\n const now = Date.now();\n const intervalTime = lastAt > 0 ? now - lastAt : 0;\n runningTime += intervalTime;\n lastAt = now;\n const state: TimerState = {\n times: ++times,\n startAt,\n stopAt,\n pauseAt,\n resumeAt,\n currentAt: now,\n elapsedTime: startAt > 0 ? now - startAt : 0,\n runningTime,\n intervalTime,\n };\n\n if (effect.length === 2) {\n effect(state, () => {\n nextTime(execute);\n });\n } else {\n effect(state);\n nextTime(execute);\n }\n };\n\n const canStart = () => status === STATUS_READY;\n const start = () => {\n if (!canStart()) return;\n status = STATUS_START;\n startAt = Date.now();\n execute();\n };\n\n const canStop = () => status === STATUS_START;\n const stop = () => {\n if (!canStop()) return;\n status = STATUS_STOP;\n stopAt = Date.now();\n };\n\n const canPause = () => status === STATUS_START;\n const pause = () => {\n if (!canPause()) return;\n status = STATUS_PAUSE;\n pauseAt = Date.now();\n };\n\n const canResume = () => status === STATUS_PAUSE;\n const resume = () => {\n if (!canResume()) return;\n status = STATUS_START;\n resumeAt = Date.now();\n lastAt = resumeAt;\n execute();\n };\n\n return {\n canStart,\n canStop,\n canPause,\n canResume,\n start,\n stop,\n pause,\n resume,\n execute,\n };\n}\n\nexport type TimerOptions = {\n /**\n * 是否在定时器开始时立即执行回调\n */\n leading?: boolean;\n /**\n * 是否在定时器停止时执行最后一次回调\n */\n trailing?: boolean;\n};\n\n/**\n * 创建一个基于 `setTimeout` 的间隔定时器\n *\n * @param callback - 每次间隔执行的回调函数,接收定时器状态和可选的 `next` 函数\n * @param interval - 间隔时间,单位为毫秒\n * @param options - 配置选项\n * @returns {TimerHandler}\n */\nexport function timeInterval(\n callback: (state: TimerState, next?: () => void) => unknown,\n interval: number,\n options?: TimerOptions,\n): TimerHandler {\n let timeId: number | NodeJS.Timeout;\n const { canStart, canStop, canPause, canResume, start, stop, pause, resume, execute } = makeInterval((call) => {\n timeId = setTimeout(call, interval);\n }, callback);\n\n return {\n start() {\n if (!canStart()) return;\n\n if (options?.leading) {\n start();\n } else {\n timeId = setTimeout(start, interval);\n }\n },\n\n stop() {\n if (!canStop()) return;\n if (options?.trailing) execute();\n\n clearTimeout(timeId);\n stop();\n },\n\n pause() {\n if (!canPause()) return;\n if (options?.trailing) execute();\n\n clearTimeout(timeId);\n pause();\n },\n\n resume(immediate?: boolean) {\n if (!canResume()) return;\n\n if (immediate || options?.leading) {\n resume();\n } else {\n timeId = setTimeout(resume, interval);\n }\n },\n };\n}\n"],"mappings":";;AA6DA,IAAM,eAAe;AACrB,IAAM,eAAe;AACrB,IAAM,eAAe;AACrB,IAAM,cAAc;;;;;;;;AASpB,SAAgB,aACd,UACA,QACA;CACA,IAAI,UAAU;CACd,IAAI,SAAS;CACb,IAAI,SAAS;CACb,IAAI,UAAU;CACd,IAAI,WAAW;CACf,IAAI,QAAQ;CACZ,IAAI,SAAS;CACb,IAAI,cAAc;CAElB,MAAM,gBAAgB;EACpB,IAAI,UAAU,cAAc;EAE5B,MAAM,MAAM,KAAK,KAAK;EACtB,MAAM,eAAe,SAAS,IAAI,MAAM,SAAS;EACjD,eAAe;EACf,SAAS;EACT,MAAM,QAAoB;GACxB,OAAO,EAAE;GACT;GACA;GACA;GACA;GACA,WAAW;GACX,aAAa,UAAU,IAAI,MAAM,UAAU;GAC3C;GACA;GACD;EAED,IAAI,OAAO,WAAW,GACpB,OAAO,aAAa;GAClB,SAAS,QAAQ;IACjB;OACG;GACL,OAAO,MAAM;GACb,SAAS,QAAQ;;;CAIrB,MAAM,iBAAiB,WAAW;CAClC,MAAM,cAAc;EAClB,IAAI,CAAC,UAAU,EAAE;EACjB,SAAS;EACT,UAAU,KAAK,KAAK;EACpB,SAAS;;CAGX,MAAM,gBAAgB,WAAW;CACjC,MAAM,aAAa;EACjB,IAAI,CAAC,SAAS,EAAE;EAChB,SAAS;EACT,SAAS,KAAK,KAAK;;CAGrB,MAAM,iBAAiB,WAAW;CAClC,MAAM,cAAc;EAClB,IAAI,CAAC,UAAU,EAAE;EACjB,SAAS;EACT,UAAU,KAAK,KAAK;;CAGtB,MAAM,kBAAkB,WAAW;CACnC,MAAM,eAAe;EACnB,IAAI,CAAC,WAAW,EAAE;EAClB,SAAS;EACT,WAAW,KAAK,KAAK;EACrB,SAAS;EACT,SAAS;;CAGX,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;;;;;;;;;AAsBH,SAAgB,aACd,UACA,UACA,SACc;CACd,IAAI;CACJ,MAAM,EAAE,UAAU,SAAS,UAAU,WAAW,OAAO,MAAM,OAAO,QAAQ,YAAY,cAAc,SAAS;EAC7G,SAAS,WAAW,MAAM,SAAS;IAClC,SAAS;CAEZ,OAAO;EACL,QAAQ;GACN,IAAI,CAAC,UAAU,EAAE;GAEjB,IAAI,SAAS,SACX,OAAO;QAEP,SAAS,WAAW,OAAO,SAAS;;EAIxC,OAAO;GACL,IAAI,CAAC,SAAS,EAAE;GAChB,IAAI,SAAS,UAAU,SAAS;GAEhC,aAAa,OAAO;GACpB,MAAM;;EAGR,QAAQ;GACN,IAAI,CAAC,UAAU,EAAE;GACjB,IAAI,SAAS,UAAU,SAAS;GAEhC,aAAa,OAAO;GACpB,OAAO;;EAGT,OAAO,WAAqB;GAC1B,IAAI,CAAC,WAAW,EAAE;GAElB,IAAI,aAAa,SAAS,SACxB,QAAQ;QAER,SAAS,WAAW,QAAQ,SAAS;;EAG1C"}
1
+ {"version":3,"file":"timer.cjs","names":[],"sources":["../src/timer.ts"],"sourcesContent":["/**\n * 定时器状态基础接口\n */\nexport type TimerStateBase = {\n /**\n * 执行次数\n */\n times: number;\n /**\n * 开始时间戳\n */\n startAt: number;\n /**\n * 停止时间戳\n */\n stopAt: number;\n /**\n * 暂停时间戳\n */\n pauseAt: number;\n /**\n * 恢复时间戳\n */\n resumeAt: number;\n /**\n * 当前时间戳\n */\n currentAt: number;\n /**\n * 总耗时(包括暂停时间)\n */\n elapsedTime: number;\n /**\n * 实际运行时间(不包括暂停时间)\n */\n runningTime: number;\n /**\n * 当前间隔时间\n */\n intervalTime: number;\n};\n\n/**\n * 定时器状态接口\n * @template T - condition 函数返回值类型,默认为 unknown\n */\nexport type TimerState<T = unknown> = TimerStateBase & {\n /**\n * condition 函数返回值,未传入 condition 时为 null\n */\n data: T;\n};\n\n/**\n * 定时器控制方法集合\n */\nexport type TimerHandler = {\n /**\n * 启动定时器\n */\n start: () => void;\n /**\n * 暂停定时器\n */\n pause: () => void;\n /**\n * 恢复定时器\n * @param immediate - 是否立即执行一次\n */\n resume: (immediate?: boolean) => void;\n /**\n * 停止定时器\n */\n stop: () => void;\n /**\n * 清除上一次定时器,立即执行,并开始下一次定时器\n */\n execute: () => void;\n};\n\n/**\n * 间隔定时器控制方法集合,包含状态查询方法\n */\nexport type IntervalHandler = TimerHandler & {\n /**\n * 是否可以启动(处于 READY 状态)\n */\n canStart: () => boolean;\n /**\n * 是否可以停止(处于 START 状态)\n */\n canStop: () => boolean;\n /**\n * 是否可以暂停(处于 START 状态)\n */\n canPause: () => boolean;\n /**\n * 是否可以恢复(处于 PAUSE 状态)\n */\n canResume: () => boolean;\n};\n\nconst STATUS_READY = 0;\nconst STATUS_START = 1;\nconst STATUS_PAUSE = 2;\nconst STATUS_STOP = 3;\n\n/**\n * makeInterval 配置选项\n * @template T - condition 函数返回值类型\n */\nexport type MakeIntervalOptions<T> = {\n /**\n * 调度器函数,用于安排下一次执行\n */\n dispatcher: (dispatch: () => void) => unknown;\n /**\n * 条件函数,每次执行前调用,返回值存入 state.data\n * 使用 MaybePromise 支持同步或异步条件判断\n * 抛错时跳过本次 runner 执行,继续下一次调度\n */\n condition?: (state: TimerStateBase) => T;\n /**\n * 执行函数,每次定时器触发时调用,接收完整的定时器状态\n * 使用 NoInfer<T> 阻断对该参数的泛型推断,确保 T 仅从 condition 返回值推断\n */\n runner: (timer: TimerState<NoInfer<Awaited<T>>>) => unknown;\n /**\n * 是否在定时器启动时立即执行一次,默认为 true\n */\n leading?: boolean;\n /**\n * 是否在定时器停止或暂停时额外执行一次(trailing edge)\n */\n trailing?: boolean;\n};\n\n/**\n * 创建可控制的间隔定时器核心函数\n *\n * @example\n * ```typescript\n * // 无 condition,state.data 为 null\n * makeInterval({\n * dispatcher: (dispatch) => setTimeout(dispatch, 1000),\n * runner: (state) => console.log(state.times),\n * })\n *\n * // 有 condition,T 自动推断为 number\n * makeInterval({\n * dispatcher: (dispatch) => setTimeout(dispatch, 1000),\n * condition: (state) => state.times,\n * runner: (state) => state.data.toFixed(2),\n * })\n * ```\n *\n * @param options - 配置选项\n * @returns 定时器控制方法集合\n */\nexport function makeInterval<T = null>(options: MakeIntervalOptions<T>): IntervalHandler {\n const { dispatcher, runner, condition, leading, trailing } = options;\n let startAt = 0;\n let lastAt = 0;\n let stopAt = 0;\n let pauseAt = 0;\n let resumeAt = 0;\n let times = 0;\n let status = STATUS_READY;\n let runningTime = 0;\n\n const execute = async () => {\n if (status >= STATUS_PAUSE) return;\n\n const now = Date.now();\n const intervalTime = lastAt > 0 ? now - lastAt : 0;\n runningTime += intervalTime;\n lastAt = now;\n const state: TimerState<T> = {\n times,\n startAt,\n stopAt,\n pauseAt,\n resumeAt,\n currentAt: now,\n elapsedTime: startAt > 0 ? now - startAt : 0,\n runningTime,\n intervalTime,\n data: null as T,\n };\n\n if (condition) {\n try {\n state.data = await condition(state);\n } catch {\n dispatcher(execute);\n return;\n }\n }\n\n state.times = ++times;\n\n await (runner as (timer: TimerState<T>) => unknown)(state);\n dispatcher(execute);\n };\n\n const canStart = () => status === STATUS_READY;\n const start = () => {\n if (!canStart()) return;\n status = STATUS_START;\n startAt = Date.now();\n if (leading === false) {\n dispatcher(execute);\n } else {\n execute();\n }\n };\n\n const canStop = () => status === STATUS_START;\n const stop = () => {\n if (!canStop()) return;\n if (trailing) execute();\n status = STATUS_STOP;\n stopAt = Date.now();\n };\n\n const canPause = () => status === STATUS_START;\n const pause = () => {\n if (!canPause()) return;\n if (trailing) execute();\n status = STATUS_PAUSE;\n pauseAt = Date.now();\n };\n\n const canResume = () => status === STATUS_PAUSE;\n const resume = () => {\n if (!canResume()) return;\n status = STATUS_START;\n resumeAt = Date.now();\n lastAt = resumeAt;\n execute();\n };\n\n return {\n canStart,\n canStop,\n canPause,\n canResume,\n start,\n stop,\n pause,\n resume,\n execute,\n };\n}\n\n/**\n * timerInterval 配置选项\n * @template T - condition 函数返回值类型\n */\nexport type TimerIntervalOptions<T> = {\n /**\n * 间隔时间,单位为毫秒\n */\n interval: number;\n /**\n * 条件函数,每次执行前调用,返回值存入 state.data\n * 抛错时跳过本次 runner 执行,继续下一次调度\n */\n condition?: (state: TimerStateBase) => T;\n /**\n * 执行函数,每次定时器触发时调用,接收完整的定时器状态\n */\n runner: (state: TimerState<NoInfer<Awaited<T>>>) => unknown;\n /**\n * 是否在定时器启动时立即执行一次,默认为 false\n */\n leading?: boolean;\n /**\n * 是否在定时器停止或暂停时额外执行一次(trailing edge)\n */\n trailing?: boolean;\n};\n\n/**\n * 创建基于 setTimeout 的间隔定时器\n *\n * @example\n * ```typescript\n * // 无 condition\n * timerInterval({\n * interval: 1000,\n * runner: (state) => console.log(state.times),\n * })\n *\n * // 有 condition,T 自动推断为 number\n * timerInterval({\n * interval: 1000,\n * condition: (state) => state.times,\n * runner: (state) => state.data.toFixed(2),\n * })\n * ```\n *\n * @param options - 配置选项\n * @returns 定时器控制方法集合\n */\nexport function timerInterval<T = null>(options: TimerIntervalOptions<T>): TimerHandler {\n const { runner, interval, condition, leading, trailing } = options;\n let timeId: number | NodeJS.Timeout;\n const { canStart, canStop, canPause, canResume, start, stop, pause, resume, execute } = makeInterval({\n dispatcher: (dispatch) => {\n timeId = setTimeout(dispatch, interval);\n },\n runner,\n condition,\n leading: leading ?? false,\n trailing,\n });\n\n return {\n start() {\n if (!canStart()) return;\n start();\n },\n\n stop() {\n if (!canStop()) return;\n stop();\n clearTimeout(timeId);\n },\n\n pause() {\n if (!canPause()) return;\n pause();\n clearTimeout(timeId);\n },\n\n resume(immediate?: boolean) {\n if (!canResume()) return;\n if (immediate || leading) {\n resume();\n } else {\n timeId = setTimeout(() => resume(), interval);\n }\n },\n\n execute() {\n clearTimeout(timeId);\n execute();\n },\n };\n}\n"],"mappings":";;AAsGA,IAAM,eAAe;AACrB,IAAM,eAAe;AACrB,IAAM,eAAe;AACrB,IAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;AAsDpB,SAAgB,aAAuB,SAAkD;CACvF,MAAM,EAAE,YAAY,QAAQ,WAAW,SAAS,aAAa;CAC7D,IAAI,UAAU;CACd,IAAI,SAAS;CACb,IAAI,SAAS;CACb,IAAI,UAAU;CACd,IAAI,WAAW;CACf,IAAI,QAAQ;CACZ,IAAI,SAAS;CACb,IAAI,cAAc;CAElB,MAAM,UAAU,YAAY;EAC1B,IAAI,UAAU,cAAc;EAE5B,MAAM,MAAM,KAAK,KAAK;EACtB,MAAM,eAAe,SAAS,IAAI,MAAM,SAAS;EACjD,eAAe;EACf,SAAS;EACT,MAAM,QAAuB;GAC3B;GACA;GACA;GACA;GACA;GACA,WAAW;GACX,aAAa,UAAU,IAAI,MAAM,UAAU;GAC3C;GACA;GACA,MAAM;GACP;EAED,IAAI,WACF,IAAI;GACF,MAAM,OAAO,MAAM,UAAU,MAAM;UAC7B;GACN,WAAW,QAAQ;GACnB;;EAIJ,MAAM,QAAQ,EAAE;EAEhB,MAAO,OAA6C,MAAM;EAC1D,WAAW,QAAQ;;CAGrB,MAAM,iBAAiB,WAAW;CAClC,MAAM,cAAc;EAClB,IAAI,CAAC,UAAU,EAAE;EACjB,SAAS;EACT,UAAU,KAAK,KAAK;EACpB,IAAI,YAAY,OACd,WAAW,QAAQ;OAEnB,SAAS;;CAIb,MAAM,gBAAgB,WAAW;CACjC,MAAM,aAAa;EACjB,IAAI,CAAC,SAAS,EAAE;EAChB,IAAI,UAAU,SAAS;EACvB,SAAS;EACT,SAAS,KAAK,KAAK;;CAGrB,MAAM,iBAAiB,WAAW;CAClC,MAAM,cAAc;EAClB,IAAI,CAAC,UAAU,EAAE;EACjB,IAAI,UAAU,SAAS;EACvB,SAAS;EACT,UAAU,KAAK,KAAK;;CAGtB,MAAM,kBAAkB,WAAW;CACnC,MAAM,eAAe;EACnB,IAAI,CAAC,WAAW,EAAE;EAClB,SAAS;EACT,WAAW,KAAK,KAAK;EACrB,SAAS;EACT,SAAS;;CAGX,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;;;;;;;;;;;;;;;;;;;;;;;AAqDH,SAAgB,cAAwB,SAAgD;CACtF,MAAM,EAAE,QAAQ,UAAU,WAAW,SAAS,aAAa;CAC3D,IAAI;CACJ,MAAM,EAAE,UAAU,SAAS,UAAU,WAAW,OAAO,MAAM,OAAO,QAAQ,YAAY,aAAa;EACnG,aAAa,aAAa;GACxB,SAAS,WAAW,UAAU,SAAS;;EAEzC;EACA;EACA,SAAS,WAAW;EACpB;EACD,CAAC;CAEF,OAAO;EACL,QAAQ;GACN,IAAI,CAAC,UAAU,EAAE;GACjB,OAAO;;EAGT,OAAO;GACL,IAAI,CAAC,SAAS,EAAE;GAChB,MAAM;GACN,aAAa,OAAO;;EAGtB,QAAQ;GACN,IAAI,CAAC,UAAU,EAAE;GACjB,OAAO;GACP,aAAa,OAAO;;EAGtB,OAAO,WAAqB;GAC1B,IAAI,CAAC,WAAW,EAAE;GAClB,IAAI,aAAa,SACf,QAAQ;QAER,SAAS,iBAAiB,QAAQ,EAAE,SAAS;;EAIjD,UAAU;GACR,aAAa,OAAO;GACpB,SAAS;;EAEZ"}
package/dist/timer.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
- * 定时器状态接口
2
+ * 定时器状态基础接口
3
3
  */
4
- export type TimerState = {
4
+ export type TimerStateBase = {
5
5
  /**
6
6
  * 执行次数
7
7
  */
@@ -39,58 +39,162 @@ export type TimerState = {
39
39
  */
40
40
  intervalTime: number;
41
41
  };
42
+ /**
43
+ * 定时器状态接口
44
+ * @template T - condition 函数返回值类型,默认为 unknown
45
+ */
46
+ export type TimerState<T = unknown> = TimerStateBase & {
47
+ /**
48
+ * condition 函数返回值,未传入 condition 时为 null
49
+ */
50
+ data: T;
51
+ };
52
+ /**
53
+ * 定时器控制方法集合
54
+ */
42
55
  export type TimerHandler = {
43
56
  /**
44
- * 开始
57
+ * 启动定时器
45
58
  */
46
59
  start: () => void;
47
60
  /**
48
- * 暂停
61
+ * 暂停定时器
49
62
  */
50
63
  pause: () => void;
51
64
  /**
52
- * 恢复
65
+ * 恢复定时器
66
+ * @param immediate - 是否立即执行一次
53
67
  */
54
68
  resume: (immediate?: boolean) => void;
55
69
  /**
56
- * 停止
70
+ * 停止定时器
57
71
  */
58
72
  stop: () => void;
73
+ /**
74
+ * 清除上一次定时器,立即执行,并开始下一次定时器
75
+ */
76
+ execute: () => void;
59
77
  };
60
78
  /**
61
- * 创建间隔定时器核心函数
62
- *
63
- * @param nextTime - 用于安排下一次执行的函数
64
- * @param effect - 每次执行的回调函数,接收定时器状态和可选的next函数
65
- * @returns 返回包含控制方法的对象
79
+ * 间隔定时器控制方法集合,包含状态查询方法
66
80
  */
67
- export declare function makeInterval(nextTime: (call: () => void) => void, effect: (timer: TimerState, next?: () => void) => unknown): {
81
+ export type IntervalHandler = TimerHandler & {
82
+ /**
83
+ * 是否可以启动(处于 READY 状态)
84
+ */
68
85
  canStart: () => boolean;
86
+ /**
87
+ * 是否可以停止(处于 START 状态)
88
+ */
69
89
  canStop: () => boolean;
90
+ /**
91
+ * 是否可以暂停(处于 START 状态)
92
+ */
70
93
  canPause: () => boolean;
94
+ /**
95
+ * 是否可以恢复(处于 PAUSE 状态)
96
+ */
71
97
  canResume: () => boolean;
72
- start: () => void;
73
- stop: () => void;
74
- pause: () => void;
75
- resume: () => void;
76
- execute: () => void;
77
98
  };
78
- export type TimerOptions = {
99
+ /**
100
+ * makeInterval 配置选项
101
+ * @template T - condition 函数返回值类型
102
+ */
103
+ export type MakeIntervalOptions<T> = {
104
+ /**
105
+ * 调度器函数,用于安排下一次执行
106
+ */
107
+ dispatcher: (dispatch: () => void) => unknown;
108
+ /**
109
+ * 条件函数,每次执行前调用,返回值存入 state.data
110
+ * 使用 MaybePromise 支持同步或异步条件判断
111
+ * 抛错时跳过本次 runner 执行,继续下一次调度
112
+ */
113
+ condition?: (state: TimerStateBase) => T;
114
+ /**
115
+ * 执行函数,每次定时器触发时调用,接收完整的定时器状态
116
+ * 使用 NoInfer<T> 阻断对该参数的泛型推断,确保 T 仅从 condition 返回值推断
117
+ */
118
+ runner: (timer: TimerState<NoInfer<Awaited<T>>>) => unknown;
119
+ /**
120
+ * 是否在定时器启动时立即执行一次,默认为 true
121
+ */
122
+ leading?: boolean;
123
+ /**
124
+ * 是否在定时器停止或暂停时额外执行一次(trailing edge)
125
+ */
126
+ trailing?: boolean;
127
+ };
128
+ /**
129
+ * 创建可控制的间隔定时器核心函数
130
+ *
131
+ * @example
132
+ * ```typescript
133
+ * // 无 condition,state.data 为 null
134
+ * makeInterval({
135
+ * dispatcher: (dispatch) => setTimeout(dispatch, 1000),
136
+ * runner: (state) => console.log(state.times),
137
+ * })
138
+ *
139
+ * // 有 condition,T 自动推断为 number
140
+ * makeInterval({
141
+ * dispatcher: (dispatch) => setTimeout(dispatch, 1000),
142
+ * condition: (state) => state.times,
143
+ * runner: (state) => state.data.toFixed(2),
144
+ * })
145
+ * ```
146
+ *
147
+ * @param options - 配置选项
148
+ * @returns 定时器控制方法集合
149
+ */
150
+ export declare function makeInterval<T = null>(options: MakeIntervalOptions<T>): IntervalHandler;
151
+ /**
152
+ * timerInterval 配置选项
153
+ * @template T - condition 函数返回值类型
154
+ */
155
+ export type TimerIntervalOptions<T> = {
156
+ /**
157
+ * 间隔时间,单位为毫秒
158
+ */
159
+ interval: number;
160
+ /**
161
+ * 条件函数,每次执行前调用,返回值存入 state.data
162
+ * 抛错时跳过本次 runner 执行,继续下一次调度
163
+ */
164
+ condition?: (state: TimerStateBase) => T;
79
165
  /**
80
- * 是否在定时器开始时立即执行回调
166
+ * 执行函数,每次定时器触发时调用,接收完整的定时器状态
167
+ */
168
+ runner: (state: TimerState<NoInfer<Awaited<T>>>) => unknown;
169
+ /**
170
+ * 是否在定时器启动时立即执行一次,默认为 false
81
171
  */
82
172
  leading?: boolean;
83
173
  /**
84
- * 是否在定时器停止时执行最后一次回调
174
+ * 是否在定时器停止或暂停时额外执行一次(trailing edge)
85
175
  */
86
176
  trailing?: boolean;
87
177
  };
88
178
  /**
89
- * 创建一个基于 `setTimeout` 的间隔定时器
179
+ * 创建基于 setTimeout 的间隔定时器
180
+ *
181
+ * @example
182
+ * ```typescript
183
+ * // 无 condition
184
+ * timerInterval({
185
+ * interval: 1000,
186
+ * runner: (state) => console.log(state.times),
187
+ * })
188
+ *
189
+ * // 有 condition,T 自动推断为 number
190
+ * timerInterval({
191
+ * interval: 1000,
192
+ * condition: (state) => state.times,
193
+ * runner: (state) => state.data.toFixed(2),
194
+ * })
195
+ * ```
90
196
  *
91
- * @param callback - 每次间隔执行的回调函数,接收定时器状态和可选的 `next` 函数
92
- * @param interval - 间隔时间,单位为毫秒
93
197
  * @param options - 配置选项
94
- * @returns {TimerHandler}
198
+ * @returns 定时器控制方法集合
95
199
  */
96
- export declare function timeInterval(callback: (state: TimerState, next?: () => void) => unknown, interval: number, options?: TimerOptions): TimerHandler;
200
+ export declare function timerInterval<T = null>(options: TimerIntervalOptions<T>): TimerHandler;
package/dist/timer.mjs CHANGED
@@ -4,13 +4,29 @@ var STATUS_START = 1;
4
4
  var STATUS_PAUSE = 2;
5
5
  var STATUS_STOP = 3;
6
6
  /**
7
- * 创建间隔定时器核心函数
7
+ * 创建可控制的间隔定时器核心函数
8
8
  *
9
- * @param nextTime - 用于安排下一次执行的函数
10
- * @param effect - 每次执行的回调函数,接收定时器状态和可选的next函数
11
- * @returns 返回包含控制方法的对象
9
+ * @example
10
+ * ```typescript
11
+ * // 无 condition,state.data 为 null
12
+ * makeInterval({
13
+ * dispatcher: (dispatch) => setTimeout(dispatch, 1000),
14
+ * runner: (state) => console.log(state.times),
15
+ * })
16
+ *
17
+ * // 有 condition,T 自动推断为 number
18
+ * makeInterval({
19
+ * dispatcher: (dispatch) => setTimeout(dispatch, 1000),
20
+ * condition: (state) => state.times,
21
+ * runner: (state) => state.data.toFixed(2),
22
+ * })
23
+ * ```
24
+ *
25
+ * @param options - 配置选项
26
+ * @returns 定时器控制方法集合
12
27
  */
13
- function makeInterval(nextTime, effect) {
28
+ function makeInterval(options) {
29
+ const { dispatcher, runner, condition, leading, trailing } = options;
14
30
  let startAt = 0;
15
31
  let lastAt = 0;
16
32
  let stopAt = 0;
@@ -19,14 +35,14 @@ function makeInterval(nextTime, effect) {
19
35
  let times = 0;
20
36
  let status = STATUS_READY;
21
37
  let runningTime = 0;
22
- const execute = () => {
38
+ const execute = async () => {
23
39
  if (status >= STATUS_PAUSE) return;
24
40
  const now = Date.now();
25
41
  const intervalTime = lastAt > 0 ? now - lastAt : 0;
26
42
  runningTime += intervalTime;
27
43
  lastAt = now;
28
44
  const state = {
29
- times: ++times,
45
+ times,
30
46
  startAt,
31
47
  stopAt,
32
48
  pauseAt,
@@ -34,32 +50,38 @@ function makeInterval(nextTime, effect) {
34
50
  currentAt: now,
35
51
  elapsedTime: startAt > 0 ? now - startAt : 0,
36
52
  runningTime,
37
- intervalTime
53
+ intervalTime,
54
+ data: null
38
55
  };
39
- if (effect.length === 2) effect(state, () => {
40
- nextTime(execute);
41
- });
42
- else {
43
- effect(state);
44
- nextTime(execute);
56
+ if (condition) try {
57
+ state.data = await condition(state);
58
+ } catch {
59
+ dispatcher(execute);
60
+ return;
45
61
  }
62
+ state.times = ++times;
63
+ await runner(state);
64
+ dispatcher(execute);
46
65
  };
47
66
  const canStart = () => status === STATUS_READY;
48
67
  const start = () => {
49
68
  if (!canStart()) return;
50
69
  status = STATUS_START;
51
70
  startAt = Date.now();
52
- execute();
71
+ if (leading === false) dispatcher(execute);
72
+ else execute();
53
73
  };
54
74
  const canStop = () => status === STATUS_START;
55
75
  const stop = () => {
56
76
  if (!canStop()) return;
77
+ if (trailing) execute();
57
78
  status = STATUS_STOP;
58
79
  stopAt = Date.now();
59
80
  };
60
81
  const canPause = () => status === STATUS_START;
61
82
  const pause = () => {
62
83
  if (!canPause()) return;
84
+ if (trailing) execute();
63
85
  status = STATUS_PAUSE;
64
86
  pauseAt = Date.now();
65
87
  };
@@ -84,44 +106,66 @@ function makeInterval(nextTime, effect) {
84
106
  };
85
107
  }
86
108
  /**
87
- * 创建一个基于 `setTimeout` 的间隔定时器
109
+ * 创建基于 setTimeout 的间隔定时器
110
+ *
111
+ * @example
112
+ * ```typescript
113
+ * // 无 condition
114
+ * timerInterval({
115
+ * interval: 1000,
116
+ * runner: (state) => console.log(state.times),
117
+ * })
118
+ *
119
+ * // 有 condition,T 自动推断为 number
120
+ * timerInterval({
121
+ * interval: 1000,
122
+ * condition: (state) => state.times,
123
+ * runner: (state) => state.data.toFixed(2),
124
+ * })
125
+ * ```
88
126
  *
89
- * @param callback - 每次间隔执行的回调函数,接收定时器状态和可选的 `next` 函数
90
- * @param interval - 间隔时间,单位为毫秒
91
127
  * @param options - 配置选项
92
- * @returns {TimerHandler}
128
+ * @returns 定时器控制方法集合
93
129
  */
94
- function timeInterval(callback, interval, options) {
130
+ function timerInterval(options) {
131
+ const { runner, interval, condition, leading, trailing } = options;
95
132
  let timeId;
96
- const { canStart, canStop, canPause, canResume, start, stop, pause, resume, execute } = makeInterval((call) => {
97
- timeId = setTimeout(call, interval);
98
- }, callback);
133
+ const { canStart, canStop, canPause, canResume, start, stop, pause, resume, execute } = makeInterval({
134
+ dispatcher: (dispatch) => {
135
+ timeId = setTimeout(dispatch, interval);
136
+ },
137
+ runner,
138
+ condition,
139
+ leading: leading ?? false,
140
+ trailing
141
+ });
99
142
  return {
100
143
  start() {
101
144
  if (!canStart()) return;
102
- if (options?.leading) start();
103
- else timeId = setTimeout(start, interval);
145
+ start();
104
146
  },
105
147
  stop() {
106
148
  if (!canStop()) return;
107
- if (options?.trailing) execute();
108
- clearTimeout(timeId);
109
149
  stop();
150
+ clearTimeout(timeId);
110
151
  },
111
152
  pause() {
112
153
  if (!canPause()) return;
113
- if (options?.trailing) execute();
114
- clearTimeout(timeId);
115
154
  pause();
155
+ clearTimeout(timeId);
116
156
  },
117
157
  resume(immediate) {
118
158
  if (!canResume()) return;
119
- if (immediate || options?.leading) resume();
120
- else timeId = setTimeout(resume, interval);
159
+ if (immediate || leading) resume();
160
+ else timeId = setTimeout(() => resume(), interval);
161
+ },
162
+ execute() {
163
+ clearTimeout(timeId);
164
+ execute();
121
165
  }
122
166
  };
123
167
  }
124
168
  //#endregion
125
- export { makeInterval, timeInterval };
169
+ export { makeInterval, timerInterval };
126
170
 
127
171
  //# sourceMappingURL=timer.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"timer.mjs","names":[],"sources":["../src/timer.ts"],"sourcesContent":["/**\n * 定时器状态接口\n */\nexport type TimerState = {\n /**\n * 执行次数\n */\n times: number;\n /**\n * 开始时间戳\n */\n startAt: number;\n /**\n * 停止时间戳\n */\n stopAt: number;\n /**\n * 暂停时间戳\n */\n pauseAt: number;\n /**\n * 恢复时间戳\n */\n resumeAt: number;\n /**\n * 当前时间戳\n */\n currentAt: number;\n /**\n * 总耗时(包括暂停时间)\n */\n elapsedTime: number;\n /**\n * 实际运行时间(不包括暂停时间)\n */\n runningTime: number;\n /**\n * 当前间隔时间\n */\n intervalTime: number;\n};\n\nexport type TimerHandler = {\n /**\n * 开始\n */\n start: () => void;\n /**\n * 暂停\n */\n pause: () => void;\n /**\n * 恢复\n */\n resume: (immediate?: boolean) => void;\n /**\n * 停止\n */\n stop: () => void;\n};\n\nconst STATUS_READY = 0;\nconst STATUS_START = 1;\nconst STATUS_PAUSE = 2;\nconst STATUS_STOP = 3;\n\n/**\n * 创建间隔定时器核心函数\n *\n * @param nextTime - 用于安排下一次执行的函数\n * @param effect - 每次执行的回调函数,接收定时器状态和可选的next函数\n * @returns 返回包含控制方法的对象\n */\nexport function makeInterval(\n nextTime: (call: () => void) => void,\n effect: (timer: TimerState, next?: () => void) => unknown,\n) {\n let startAt = 0;\n let lastAt = 0;\n let stopAt = 0;\n let pauseAt = 0;\n let resumeAt = 0;\n let times = 0;\n let status = STATUS_READY;\n let runningTime = 0;\n\n const execute = () => {\n if (status >= STATUS_PAUSE) return;\n\n const now = Date.now();\n const intervalTime = lastAt > 0 ? now - lastAt : 0;\n runningTime += intervalTime;\n lastAt = now;\n const state: TimerState = {\n times: ++times,\n startAt,\n stopAt,\n pauseAt,\n resumeAt,\n currentAt: now,\n elapsedTime: startAt > 0 ? now - startAt : 0,\n runningTime,\n intervalTime,\n };\n\n if (effect.length === 2) {\n effect(state, () => {\n nextTime(execute);\n });\n } else {\n effect(state);\n nextTime(execute);\n }\n };\n\n const canStart = () => status === STATUS_READY;\n const start = () => {\n if (!canStart()) return;\n status = STATUS_START;\n startAt = Date.now();\n execute();\n };\n\n const canStop = () => status === STATUS_START;\n const stop = () => {\n if (!canStop()) return;\n status = STATUS_STOP;\n stopAt = Date.now();\n };\n\n const canPause = () => status === STATUS_START;\n const pause = () => {\n if (!canPause()) return;\n status = STATUS_PAUSE;\n pauseAt = Date.now();\n };\n\n const canResume = () => status === STATUS_PAUSE;\n const resume = () => {\n if (!canResume()) return;\n status = STATUS_START;\n resumeAt = Date.now();\n lastAt = resumeAt;\n execute();\n };\n\n return {\n canStart,\n canStop,\n canPause,\n canResume,\n start,\n stop,\n pause,\n resume,\n execute,\n };\n}\n\nexport type TimerOptions = {\n /**\n * 是否在定时器开始时立即执行回调\n */\n leading?: boolean;\n /**\n * 是否在定时器停止时执行最后一次回调\n */\n trailing?: boolean;\n};\n\n/**\n * 创建一个基于 `setTimeout` 的间隔定时器\n *\n * @param callback - 每次间隔执行的回调函数,接收定时器状态和可选的 `next` 函数\n * @param interval - 间隔时间,单位为毫秒\n * @param options - 配置选项\n * @returns {TimerHandler}\n */\nexport function timeInterval(\n callback: (state: TimerState, next?: () => void) => unknown,\n interval: number,\n options?: TimerOptions,\n): TimerHandler {\n let timeId: number | NodeJS.Timeout;\n const { canStart, canStop, canPause, canResume, start, stop, pause, resume, execute } = makeInterval((call) => {\n timeId = setTimeout(call, interval);\n }, callback);\n\n return {\n start() {\n if (!canStart()) return;\n\n if (options?.leading) {\n start();\n } else {\n timeId = setTimeout(start, interval);\n }\n },\n\n stop() {\n if (!canStop()) return;\n if (options?.trailing) execute();\n\n clearTimeout(timeId);\n stop();\n },\n\n pause() {\n if (!canPause()) return;\n if (options?.trailing) execute();\n\n clearTimeout(timeId);\n pause();\n },\n\n resume(immediate?: boolean) {\n if (!canResume()) return;\n\n if (immediate || options?.leading) {\n resume();\n } else {\n timeId = setTimeout(resume, interval);\n }\n },\n };\n}\n"],"mappings":";AA6DA,IAAM,eAAe;AACrB,IAAM,eAAe;AACrB,IAAM,eAAe;AACrB,IAAM,cAAc;;;;;;;;AASpB,SAAgB,aACd,UACA,QACA;CACA,IAAI,UAAU;CACd,IAAI,SAAS;CACb,IAAI,SAAS;CACb,IAAI,UAAU;CACd,IAAI,WAAW;CACf,IAAI,QAAQ;CACZ,IAAI,SAAS;CACb,IAAI,cAAc;CAElB,MAAM,gBAAgB;EACpB,IAAI,UAAU,cAAc;EAE5B,MAAM,MAAM,KAAK,KAAK;EACtB,MAAM,eAAe,SAAS,IAAI,MAAM,SAAS;EACjD,eAAe;EACf,SAAS;EACT,MAAM,QAAoB;GACxB,OAAO,EAAE;GACT;GACA;GACA;GACA;GACA,WAAW;GACX,aAAa,UAAU,IAAI,MAAM,UAAU;GAC3C;GACA;GACD;EAED,IAAI,OAAO,WAAW,GACpB,OAAO,aAAa;GAClB,SAAS,QAAQ;IACjB;OACG;GACL,OAAO,MAAM;GACb,SAAS,QAAQ;;;CAIrB,MAAM,iBAAiB,WAAW;CAClC,MAAM,cAAc;EAClB,IAAI,CAAC,UAAU,EAAE;EACjB,SAAS;EACT,UAAU,KAAK,KAAK;EACpB,SAAS;;CAGX,MAAM,gBAAgB,WAAW;CACjC,MAAM,aAAa;EACjB,IAAI,CAAC,SAAS,EAAE;EAChB,SAAS;EACT,SAAS,KAAK,KAAK;;CAGrB,MAAM,iBAAiB,WAAW;CAClC,MAAM,cAAc;EAClB,IAAI,CAAC,UAAU,EAAE;EACjB,SAAS;EACT,UAAU,KAAK,KAAK;;CAGtB,MAAM,kBAAkB,WAAW;CACnC,MAAM,eAAe;EACnB,IAAI,CAAC,WAAW,EAAE;EAClB,SAAS;EACT,WAAW,KAAK,KAAK;EACrB,SAAS;EACT,SAAS;;CAGX,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;;;;;;;;;AAsBH,SAAgB,aACd,UACA,UACA,SACc;CACd,IAAI;CACJ,MAAM,EAAE,UAAU,SAAS,UAAU,WAAW,OAAO,MAAM,OAAO,QAAQ,YAAY,cAAc,SAAS;EAC7G,SAAS,WAAW,MAAM,SAAS;IAClC,SAAS;CAEZ,OAAO;EACL,QAAQ;GACN,IAAI,CAAC,UAAU,EAAE;GAEjB,IAAI,SAAS,SACX,OAAO;QAEP,SAAS,WAAW,OAAO,SAAS;;EAIxC,OAAO;GACL,IAAI,CAAC,SAAS,EAAE;GAChB,IAAI,SAAS,UAAU,SAAS;GAEhC,aAAa,OAAO;GACpB,MAAM;;EAGR,QAAQ;GACN,IAAI,CAAC,UAAU,EAAE;GACjB,IAAI,SAAS,UAAU,SAAS;GAEhC,aAAa,OAAO;GACpB,OAAO;;EAGT,OAAO,WAAqB;GAC1B,IAAI,CAAC,WAAW,EAAE;GAElB,IAAI,aAAa,SAAS,SACxB,QAAQ;QAER,SAAS,WAAW,QAAQ,SAAS;;EAG1C"}
1
+ {"version":3,"file":"timer.mjs","names":[],"sources":["../src/timer.ts"],"sourcesContent":["/**\n * 定时器状态基础接口\n */\nexport type TimerStateBase = {\n /**\n * 执行次数\n */\n times: number;\n /**\n * 开始时间戳\n */\n startAt: number;\n /**\n * 停止时间戳\n */\n stopAt: number;\n /**\n * 暂停时间戳\n */\n pauseAt: number;\n /**\n * 恢复时间戳\n */\n resumeAt: number;\n /**\n * 当前时间戳\n */\n currentAt: number;\n /**\n * 总耗时(包括暂停时间)\n */\n elapsedTime: number;\n /**\n * 实际运行时间(不包括暂停时间)\n */\n runningTime: number;\n /**\n * 当前间隔时间\n */\n intervalTime: number;\n};\n\n/**\n * 定时器状态接口\n * @template T - condition 函数返回值类型,默认为 unknown\n */\nexport type TimerState<T = unknown> = TimerStateBase & {\n /**\n * condition 函数返回值,未传入 condition 时为 null\n */\n data: T;\n};\n\n/**\n * 定时器控制方法集合\n */\nexport type TimerHandler = {\n /**\n * 启动定时器\n */\n start: () => void;\n /**\n * 暂停定时器\n */\n pause: () => void;\n /**\n * 恢复定时器\n * @param immediate - 是否立即执行一次\n */\n resume: (immediate?: boolean) => void;\n /**\n * 停止定时器\n */\n stop: () => void;\n /**\n * 清除上一次定时器,立即执行,并开始下一次定时器\n */\n execute: () => void;\n};\n\n/**\n * 间隔定时器控制方法集合,包含状态查询方法\n */\nexport type IntervalHandler = TimerHandler & {\n /**\n * 是否可以启动(处于 READY 状态)\n */\n canStart: () => boolean;\n /**\n * 是否可以停止(处于 START 状态)\n */\n canStop: () => boolean;\n /**\n * 是否可以暂停(处于 START 状态)\n */\n canPause: () => boolean;\n /**\n * 是否可以恢复(处于 PAUSE 状态)\n */\n canResume: () => boolean;\n};\n\nconst STATUS_READY = 0;\nconst STATUS_START = 1;\nconst STATUS_PAUSE = 2;\nconst STATUS_STOP = 3;\n\n/**\n * makeInterval 配置选项\n * @template T - condition 函数返回值类型\n */\nexport type MakeIntervalOptions<T> = {\n /**\n * 调度器函数,用于安排下一次执行\n */\n dispatcher: (dispatch: () => void) => unknown;\n /**\n * 条件函数,每次执行前调用,返回值存入 state.data\n * 使用 MaybePromise 支持同步或异步条件判断\n * 抛错时跳过本次 runner 执行,继续下一次调度\n */\n condition?: (state: TimerStateBase) => T;\n /**\n * 执行函数,每次定时器触发时调用,接收完整的定时器状态\n * 使用 NoInfer<T> 阻断对该参数的泛型推断,确保 T 仅从 condition 返回值推断\n */\n runner: (timer: TimerState<NoInfer<Awaited<T>>>) => unknown;\n /**\n * 是否在定时器启动时立即执行一次,默认为 true\n */\n leading?: boolean;\n /**\n * 是否在定时器停止或暂停时额外执行一次(trailing edge)\n */\n trailing?: boolean;\n};\n\n/**\n * 创建可控制的间隔定时器核心函数\n *\n * @example\n * ```typescript\n * // 无 condition,state.data 为 null\n * makeInterval({\n * dispatcher: (dispatch) => setTimeout(dispatch, 1000),\n * runner: (state) => console.log(state.times),\n * })\n *\n * // 有 condition,T 自动推断为 number\n * makeInterval({\n * dispatcher: (dispatch) => setTimeout(dispatch, 1000),\n * condition: (state) => state.times,\n * runner: (state) => state.data.toFixed(2),\n * })\n * ```\n *\n * @param options - 配置选项\n * @returns 定时器控制方法集合\n */\nexport function makeInterval<T = null>(options: MakeIntervalOptions<T>): IntervalHandler {\n const { dispatcher, runner, condition, leading, trailing } = options;\n let startAt = 0;\n let lastAt = 0;\n let stopAt = 0;\n let pauseAt = 0;\n let resumeAt = 0;\n let times = 0;\n let status = STATUS_READY;\n let runningTime = 0;\n\n const execute = async () => {\n if (status >= STATUS_PAUSE) return;\n\n const now = Date.now();\n const intervalTime = lastAt > 0 ? now - lastAt : 0;\n runningTime += intervalTime;\n lastAt = now;\n const state: TimerState<T> = {\n times,\n startAt,\n stopAt,\n pauseAt,\n resumeAt,\n currentAt: now,\n elapsedTime: startAt > 0 ? now - startAt : 0,\n runningTime,\n intervalTime,\n data: null as T,\n };\n\n if (condition) {\n try {\n state.data = await condition(state);\n } catch {\n dispatcher(execute);\n return;\n }\n }\n\n state.times = ++times;\n\n await (runner as (timer: TimerState<T>) => unknown)(state);\n dispatcher(execute);\n };\n\n const canStart = () => status === STATUS_READY;\n const start = () => {\n if (!canStart()) return;\n status = STATUS_START;\n startAt = Date.now();\n if (leading === false) {\n dispatcher(execute);\n } else {\n execute();\n }\n };\n\n const canStop = () => status === STATUS_START;\n const stop = () => {\n if (!canStop()) return;\n if (trailing) execute();\n status = STATUS_STOP;\n stopAt = Date.now();\n };\n\n const canPause = () => status === STATUS_START;\n const pause = () => {\n if (!canPause()) return;\n if (trailing) execute();\n status = STATUS_PAUSE;\n pauseAt = Date.now();\n };\n\n const canResume = () => status === STATUS_PAUSE;\n const resume = () => {\n if (!canResume()) return;\n status = STATUS_START;\n resumeAt = Date.now();\n lastAt = resumeAt;\n execute();\n };\n\n return {\n canStart,\n canStop,\n canPause,\n canResume,\n start,\n stop,\n pause,\n resume,\n execute,\n };\n}\n\n/**\n * timerInterval 配置选项\n * @template T - condition 函数返回值类型\n */\nexport type TimerIntervalOptions<T> = {\n /**\n * 间隔时间,单位为毫秒\n */\n interval: number;\n /**\n * 条件函数,每次执行前调用,返回值存入 state.data\n * 抛错时跳过本次 runner 执行,继续下一次调度\n */\n condition?: (state: TimerStateBase) => T;\n /**\n * 执行函数,每次定时器触发时调用,接收完整的定时器状态\n */\n runner: (state: TimerState<NoInfer<Awaited<T>>>) => unknown;\n /**\n * 是否在定时器启动时立即执行一次,默认为 false\n */\n leading?: boolean;\n /**\n * 是否在定时器停止或暂停时额外执行一次(trailing edge)\n */\n trailing?: boolean;\n};\n\n/**\n * 创建基于 setTimeout 的间隔定时器\n *\n * @example\n * ```typescript\n * // 无 condition\n * timerInterval({\n * interval: 1000,\n * runner: (state) => console.log(state.times),\n * })\n *\n * // 有 condition,T 自动推断为 number\n * timerInterval({\n * interval: 1000,\n * condition: (state) => state.times,\n * runner: (state) => state.data.toFixed(2),\n * })\n * ```\n *\n * @param options - 配置选项\n * @returns 定时器控制方法集合\n */\nexport function timerInterval<T = null>(options: TimerIntervalOptions<T>): TimerHandler {\n const { runner, interval, condition, leading, trailing } = options;\n let timeId: number | NodeJS.Timeout;\n const { canStart, canStop, canPause, canResume, start, stop, pause, resume, execute } = makeInterval({\n dispatcher: (dispatch) => {\n timeId = setTimeout(dispatch, interval);\n },\n runner,\n condition,\n leading: leading ?? false,\n trailing,\n });\n\n return {\n start() {\n if (!canStart()) return;\n start();\n },\n\n stop() {\n if (!canStop()) return;\n stop();\n clearTimeout(timeId);\n },\n\n pause() {\n if (!canPause()) return;\n pause();\n clearTimeout(timeId);\n },\n\n resume(immediate?: boolean) {\n if (!canResume()) return;\n if (immediate || leading) {\n resume();\n } else {\n timeId = setTimeout(() => resume(), interval);\n }\n },\n\n execute() {\n clearTimeout(timeId);\n execute();\n },\n };\n}\n"],"mappings":";AAsGA,IAAM,eAAe;AACrB,IAAM,eAAe;AACrB,IAAM,eAAe;AACrB,IAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;AAsDpB,SAAgB,aAAuB,SAAkD;CACvF,MAAM,EAAE,YAAY,QAAQ,WAAW,SAAS,aAAa;CAC7D,IAAI,UAAU;CACd,IAAI,SAAS;CACb,IAAI,SAAS;CACb,IAAI,UAAU;CACd,IAAI,WAAW;CACf,IAAI,QAAQ;CACZ,IAAI,SAAS;CACb,IAAI,cAAc;CAElB,MAAM,UAAU,YAAY;EAC1B,IAAI,UAAU,cAAc;EAE5B,MAAM,MAAM,KAAK,KAAK;EACtB,MAAM,eAAe,SAAS,IAAI,MAAM,SAAS;EACjD,eAAe;EACf,SAAS;EACT,MAAM,QAAuB;GAC3B;GACA;GACA;GACA;GACA;GACA,WAAW;GACX,aAAa,UAAU,IAAI,MAAM,UAAU;GAC3C;GACA;GACA,MAAM;GACP;EAED,IAAI,WACF,IAAI;GACF,MAAM,OAAO,MAAM,UAAU,MAAM;UAC7B;GACN,WAAW,QAAQ;GACnB;;EAIJ,MAAM,QAAQ,EAAE;EAEhB,MAAO,OAA6C,MAAM;EAC1D,WAAW,QAAQ;;CAGrB,MAAM,iBAAiB,WAAW;CAClC,MAAM,cAAc;EAClB,IAAI,CAAC,UAAU,EAAE;EACjB,SAAS;EACT,UAAU,KAAK,KAAK;EACpB,IAAI,YAAY,OACd,WAAW,QAAQ;OAEnB,SAAS;;CAIb,MAAM,gBAAgB,WAAW;CACjC,MAAM,aAAa;EACjB,IAAI,CAAC,SAAS,EAAE;EAChB,IAAI,UAAU,SAAS;EACvB,SAAS;EACT,SAAS,KAAK,KAAK;;CAGrB,MAAM,iBAAiB,WAAW;CAClC,MAAM,cAAc;EAClB,IAAI,CAAC,UAAU,EAAE;EACjB,IAAI,UAAU,SAAS;EACvB,SAAS;EACT,UAAU,KAAK,KAAK;;CAGtB,MAAM,kBAAkB,WAAW;CACnC,MAAM,eAAe;EACnB,IAAI,CAAC,WAAW,EAAE;EAClB,SAAS;EACT,WAAW,KAAK,KAAK;EACrB,SAAS;EACT,SAAS;;CAGX,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;;;;;;;;;;;;;;;;;;;;;;;AAqDH,SAAgB,cAAwB,SAAgD;CACtF,MAAM,EAAE,QAAQ,UAAU,WAAW,SAAS,aAAa;CAC3D,IAAI;CACJ,MAAM,EAAE,UAAU,SAAS,UAAU,WAAW,OAAO,MAAM,OAAO,QAAQ,YAAY,aAAa;EACnG,aAAa,aAAa;GACxB,SAAS,WAAW,UAAU,SAAS;;EAEzC;EACA;EACA,SAAS,WAAW;EACpB;EACD,CAAC;CAEF,OAAO;EACL,QAAQ;GACN,IAAI,CAAC,UAAU,EAAE;GACjB,OAAO;;EAGT,OAAO;GACL,IAAI,CAAC,SAAS,EAAE;GAChB,MAAM;GACN,aAAa,OAAO;;EAGtB,QAAQ;GACN,IAAI,CAAC,UAAU,EAAE;GACjB,OAAO;GACP,aAAa,OAAO;;EAGtB,OAAO,WAAqB;GAC1B,IAAI,CAAC,WAAW,EAAE;GAClB,IAAI,aAAa,SACf,QAAQ;QAER,SAAS,iBAAiB,QAAQ,EAAE,SAAS;;EAIjD,UAAU;GACR,aAAa,OAAO;GACpB,SAAS;;EAEZ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudcome/utils-core",
3
- "version": "1.20.1",
3
+ "version": "1.22.0",
4
4
  "description": "cloudcome core utils",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",