@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 +33 -0
- package/dist/function.cjs.map +1 -1
- package/dist/function.d.ts +34 -0
- package/dist/function.mjs +33 -1
- package/dist/function.mjs.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/timer.cjs +76 -32
- package/dist/timer.cjs.map +1 -1
- package/dist/timer.d.ts +129 -25
- package/dist/timer.mjs +76 -32
- package/dist/timer.mjs.map +1 -1
- package/package.json +1 -1
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
|
package/dist/function.cjs.map
CHANGED
|
@@ -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":"
|
|
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"}
|
package/dist/function.d.ts
CHANGED
|
@@ -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
|
package/dist/function.mjs.map
CHANGED
|
@@ -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":"
|
|
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
package/dist/index.mjs
CHANGED
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
|
-
* @
|
|
11
|
-
*
|
|
12
|
-
*
|
|
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(
|
|
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
|
|
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 (
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
|
129
|
+
* @returns 定时器控制方法集合
|
|
94
130
|
*/
|
|
95
|
-
function
|
|
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(
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
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 ||
|
|
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.
|
|
171
|
+
exports.timerInterval = timerInterval;
|
|
128
172
|
|
|
129
173
|
//# sourceMappingURL=timer.cjs.map
|
package/dist/timer.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timer.cjs","names":[],"sources":["../src/timer.ts"],"sourcesContent":["/**\n *
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
|
198
|
+
* @returns 定时器控制方法集合
|
|
95
199
|
*/
|
|
96
|
-
export declare function
|
|
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
|
-
* @
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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(
|
|
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
|
|
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 (
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
|
128
|
+
* @returns 定时器控制方法集合
|
|
93
129
|
*/
|
|
94
|
-
function
|
|
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(
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
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 ||
|
|
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,
|
|
169
|
+
export { makeInterval, timerInterval };
|
|
126
170
|
|
|
127
171
|
//# sourceMappingURL=timer.mjs.map
|
package/dist/timer.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timer.mjs","names":[],"sources":["../src/timer.ts"],"sourcesContent":["/**\n *
|
|
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"}
|