@tanstack/pacer 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/async-debouncer.cjs +24 -13
- package/dist/cjs/async-debouncer.cjs.map +1 -1
- package/dist/cjs/async-debouncer.d.cts +49 -7
- package/dist/cjs/async-queuer.cjs +83 -114
- package/dist/cjs/async-queuer.cjs.map +1 -1
- package/dist/cjs/async-queuer.d.cts +79 -50
- package/dist/cjs/async-rate-limiter.cjs +20 -9
- package/dist/cjs/async-rate-limiter.cjs.map +1 -1
- package/dist/cjs/async-rate-limiter.d.cts +53 -3
- package/dist/cjs/async-throttler.cjs +24 -13
- package/dist/cjs/async-throttler.cjs.map +1 -1
- package/dist/cjs/async-throttler.d.cts +48 -6
- package/dist/cjs/types.d.cts +1 -0
- package/dist/esm/async-debouncer.d.ts +49 -7
- package/dist/esm/async-debouncer.js +24 -13
- package/dist/esm/async-debouncer.js.map +1 -1
- package/dist/esm/async-queuer.d.ts +79 -50
- package/dist/esm/async-queuer.js +83 -114
- package/dist/esm/async-queuer.js.map +1 -1
- package/dist/esm/async-rate-limiter.d.ts +53 -3
- package/dist/esm/async-rate-limiter.js +20 -9
- package/dist/esm/async-rate-limiter.js.map +1 -1
- package/dist/esm/async-throttler.d.ts +48 -6
- package/dist/esm/async-throttler.js +24 -13
- package/dist/esm/async-throttler.js.map +1 -1
- package/dist/esm/types.d.ts +1 -0
- package/package.json +1 -1
- package/src/async-debouncer.ts +66 -16
- package/src/async-queuer.ts +180 -169
- package/src/async-rate-limiter.ts +70 -12
- package/src/async-throttler.ts +66 -16
- package/src/types.ts +3 -0
package/src/async-debouncer.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parseFunctionOrValue } from './utils'
|
|
2
|
-
import type { AnyAsyncFunction } from './types'
|
|
2
|
+
import type { AnyAsyncFunction, OptionalKeys } from './types'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Options for configuring an async debounced function
|
|
@@ -17,7 +17,9 @@ export interface AsyncDebouncerOptions<TFn extends AnyAsyncFunction> {
|
|
|
17
17
|
*/
|
|
18
18
|
leading?: boolean
|
|
19
19
|
/**
|
|
20
|
-
* Optional error handler for when the debounced function throws
|
|
20
|
+
* Optional error handler for when the debounced function throws.
|
|
21
|
+
* If provided, the handler will be called with the error and debouncer instance.
|
|
22
|
+
* This can be used alongside throwOnError - the handler will be called before any error is thrown.
|
|
21
23
|
*/
|
|
22
24
|
onError?: (error: unknown, debouncer: AsyncDebouncer<TFn>) => void
|
|
23
25
|
/**
|
|
@@ -28,6 +30,12 @@ export interface AsyncDebouncerOptions<TFn extends AnyAsyncFunction> {
|
|
|
28
30
|
* Optional callback to call when the debounced function is executed
|
|
29
31
|
*/
|
|
30
32
|
onSuccess?: (result: ReturnType<TFn>, debouncer: AsyncDebouncer<TFn>) => void
|
|
33
|
+
/**
|
|
34
|
+
* Whether to throw errors when they occur.
|
|
35
|
+
* Defaults to true if no onError handler is provided, false if an onError handler is provided.
|
|
36
|
+
* Can be explicitly set to override these defaults.
|
|
37
|
+
*/
|
|
38
|
+
throwOnError?: boolean
|
|
31
39
|
/**
|
|
32
40
|
* Whether to execute on the trailing edge of the timeout.
|
|
33
41
|
* Defaults to true.
|
|
@@ -41,12 +49,14 @@ export interface AsyncDebouncerOptions<TFn extends AnyAsyncFunction> {
|
|
|
41
49
|
wait: number | ((debouncer: AsyncDebouncer<TFn>) => number)
|
|
42
50
|
}
|
|
43
51
|
|
|
44
|
-
|
|
52
|
+
type AsyncDebouncerOptionsWithOptionalCallbacks = OptionalKeys<
|
|
53
|
+
AsyncDebouncerOptions<any>,
|
|
54
|
+
'onError' | 'onSettled' | 'onSuccess'
|
|
55
|
+
>
|
|
56
|
+
|
|
57
|
+
const defaultOptions: AsyncDebouncerOptionsWithOptionalCallbacks = {
|
|
45
58
|
enabled: true,
|
|
46
59
|
leading: false,
|
|
47
|
-
onError: () => {},
|
|
48
|
-
onSettled: () => {},
|
|
49
|
-
onSuccess: () => {},
|
|
50
60
|
trailing: true,
|
|
51
61
|
wait: 0,
|
|
52
62
|
}
|
|
@@ -65,12 +75,23 @@ const defaultOptions: Required<AsyncDebouncerOptions<any>> = {
|
|
|
65
75
|
* making it ideal for API calls and other async operations where you want the result of the `maybeExecute` call
|
|
66
76
|
* instead of setting the result on a state variable from within the debounced function.
|
|
67
77
|
*
|
|
78
|
+
* Error Handling:
|
|
79
|
+
* - If an error occurs during execution and no `onError` handler is provided, the error will be thrown and propagate up to the caller.
|
|
80
|
+
* - If an `onError` handler is provided, errors will be caught and passed to the handler instead of being thrown.
|
|
81
|
+
* - The error count can be tracked using `getErrorCount()`.
|
|
82
|
+
* - The debouncer maintains its state and can continue to be used after an error occurs.
|
|
83
|
+
*
|
|
68
84
|
* @example
|
|
69
85
|
* ```ts
|
|
70
86
|
* const asyncDebouncer = new AsyncDebouncer(async (value: string) => {
|
|
71
87
|
* const results = await searchAPI(value);
|
|
72
88
|
* return results; // Return value is preserved
|
|
73
|
-
* }, {
|
|
89
|
+
* }, {
|
|
90
|
+
* wait: 500,
|
|
91
|
+
* onError: (error) => {
|
|
92
|
+
* console.error('Search failed:', error);
|
|
93
|
+
* }
|
|
94
|
+
* });
|
|
74
95
|
*
|
|
75
96
|
* // Called on each keystroke but only executes after 500ms of no typing
|
|
76
97
|
* // Returns the API response directly
|
|
@@ -78,6 +99,7 @@ const defaultOptions: Required<AsyncDebouncerOptions<any>> = {
|
|
|
78
99
|
* ```
|
|
79
100
|
*/
|
|
80
101
|
export class AsyncDebouncer<TFn extends AnyAsyncFunction> {
|
|
102
|
+
private _options: AsyncDebouncerOptionsWithOptionalCallbacks
|
|
81
103
|
private _abortController: AbortController | null = null
|
|
82
104
|
private _canLeadingExecute = true
|
|
83
105
|
private _errorCount = 0
|
|
@@ -85,7 +107,6 @@ export class AsyncDebouncer<TFn extends AnyAsyncFunction> {
|
|
|
85
107
|
private _isPending = false
|
|
86
108
|
private _lastArgs: Parameters<TFn> | undefined
|
|
87
109
|
private _lastResult: ReturnType<TFn> | undefined
|
|
88
|
-
private _options: Required<AsyncDebouncerOptions<TFn>>
|
|
89
110
|
private _settleCount = 0
|
|
90
111
|
private _successCount = 0
|
|
91
112
|
private _timeoutId: NodeJS.Timeout | null = null
|
|
@@ -97,6 +118,7 @@ export class AsyncDebouncer<TFn extends AnyAsyncFunction> {
|
|
|
97
118
|
this._options = {
|
|
98
119
|
...defaultOptions,
|
|
99
120
|
...initialOptions,
|
|
121
|
+
throwOnError: initialOptions.throwOnError ?? !initialOptions.onError,
|
|
100
122
|
}
|
|
101
123
|
}
|
|
102
124
|
|
|
@@ -116,7 +138,7 @@ export class AsyncDebouncer<TFn extends AnyAsyncFunction> {
|
|
|
116
138
|
/**
|
|
117
139
|
* Returns the current debouncer options
|
|
118
140
|
*/
|
|
119
|
-
getOptions():
|
|
141
|
+
getOptions(): AsyncDebouncerOptions<TFn> {
|
|
120
142
|
return this._options
|
|
121
143
|
}
|
|
122
144
|
|
|
@@ -124,7 +146,7 @@ export class AsyncDebouncer<TFn extends AnyAsyncFunction> {
|
|
|
124
146
|
* Returns the current debouncer enabled state
|
|
125
147
|
*/
|
|
126
148
|
getEnabled(): boolean {
|
|
127
|
-
return parseFunctionOrValue(this._options.enabled, this)
|
|
149
|
+
return !!parseFunctionOrValue(this._options.enabled, this)
|
|
128
150
|
}
|
|
129
151
|
|
|
130
152
|
/**
|
|
@@ -135,8 +157,18 @@ export class AsyncDebouncer<TFn extends AnyAsyncFunction> {
|
|
|
135
157
|
}
|
|
136
158
|
|
|
137
159
|
/**
|
|
138
|
-
* Attempts to execute the debounced function
|
|
139
|
-
* If a call is already in progress, it will be queued
|
|
160
|
+
* Attempts to execute the debounced function.
|
|
161
|
+
* If a call is already in progress, it will be queued.
|
|
162
|
+
*
|
|
163
|
+
* Error Handling:
|
|
164
|
+
* - If the debounced function throws and no `onError` handler is configured,
|
|
165
|
+
* the error will be thrown from this method.
|
|
166
|
+
* - If an `onError` handler is configured, errors will be caught and passed to the handler,
|
|
167
|
+
* and this method will return undefined.
|
|
168
|
+
* - The error state can be checked using `getErrorCount()` and `getIsExecuting()`.
|
|
169
|
+
*
|
|
170
|
+
* @returns A promise that resolves with the function's return value, or undefined if an error occurred and was handled by onError
|
|
171
|
+
* @throws The error from the debounced function if no onError handler is configured
|
|
140
172
|
*/
|
|
141
173
|
async maybeExecute(
|
|
142
174
|
...args: Parameters<TFn>
|
|
@@ -179,16 +211,21 @@ export class AsyncDebouncer<TFn extends AnyAsyncFunction> {
|
|
|
179
211
|
this._isExecuting = true
|
|
180
212
|
this._lastResult = await this.fn(...args) // EXECUTE!
|
|
181
213
|
this._successCount++
|
|
182
|
-
this._options.onSuccess(this._lastResult!, this)
|
|
214
|
+
this._options.onSuccess?.(this._lastResult!, this)
|
|
183
215
|
} catch (error) {
|
|
184
216
|
this._errorCount++
|
|
185
|
-
this._options.onError(error, this)
|
|
217
|
+
this._options.onError?.(error, this)
|
|
218
|
+
if (this._options.throwOnError) {
|
|
219
|
+
throw error
|
|
220
|
+
} else {
|
|
221
|
+
console.error(error)
|
|
222
|
+
}
|
|
186
223
|
} finally {
|
|
187
224
|
this._isExecuting = false
|
|
188
225
|
this._isPending = false
|
|
189
226
|
this._settleCount++
|
|
190
227
|
this._abortController = null
|
|
191
|
-
this._options.onSettled(this)
|
|
228
|
+
this._options.onSettled?.(this)
|
|
192
229
|
}
|
|
193
230
|
return this._lastResult
|
|
194
231
|
}
|
|
@@ -270,12 +307,25 @@ export class AsyncDebouncer<TFn extends AnyAsyncFunction> {
|
|
|
270
307
|
* making it ideal for API calls and other async operations where you want the result of the `maybeExecute` call
|
|
271
308
|
* instead of setting the result on a state variable from within the debounced function.
|
|
272
309
|
*
|
|
310
|
+
* Error Handling:
|
|
311
|
+
* - If an `onError` handler is provided, it will be called with the error and debouncer instance
|
|
312
|
+
* - If `throwOnError` is true (default when no onError handler is provided), the error will be thrown
|
|
313
|
+
* - If `throwOnError` is false (default when onError handler is provided), the error will be swallowed
|
|
314
|
+
* - The error state can be checked using the underlying AsyncDebouncer instance
|
|
315
|
+
* - Both onError and throwOnError can be used together - the handler will be called before any error is thrown
|
|
316
|
+
*
|
|
273
317
|
* @example
|
|
274
318
|
* ```ts
|
|
275
319
|
* const debounced = asyncDebounce(async (value: string) => {
|
|
276
320
|
* const result = await saveToAPI(value);
|
|
277
321
|
* return result; // Return value is preserved
|
|
278
|
-
* }, {
|
|
322
|
+
* }, {
|
|
323
|
+
* wait: 1000,
|
|
324
|
+
* onError: (error) => {
|
|
325
|
+
* console.error('API call failed:', error);
|
|
326
|
+
* },
|
|
327
|
+
* throwOnError: true // Will both log the error and throw it
|
|
328
|
+
* });
|
|
279
329
|
*
|
|
280
330
|
* // Will only execute once, 1 second after the last call
|
|
281
331
|
* // Returns the API response directly
|