@tanstack/pacer 0.1.0 → 0.2.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 +60 -44
- package/dist/cjs/async-debouncer.cjs.map +1 -1
- package/dist/cjs/async-debouncer.d.cts +37 -24
- package/dist/cjs/async-queuer.cjs +149 -125
- package/dist/cjs/async-queuer.cjs.map +1 -1
- package/dist/cjs/async-queuer.d.cts +65 -48
- package/dist/cjs/async-rate-limiter.cjs +63 -46
- package/dist/cjs/async-rate-limiter.cjs.map +1 -1
- package/dist/cjs/async-rate-limiter.d.cts +39 -27
- package/dist/cjs/async-throttler.cjs +70 -47
- package/dist/cjs/async-throttler.cjs.map +1 -1
- package/dist/cjs/async-throttler.d.cts +43 -25
- package/dist/cjs/debouncer.cjs +46 -22
- package/dist/cjs/debouncer.cjs.map +1 -1
- package/dist/cjs/debouncer.d.cts +25 -11
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +2 -0
- package/dist/cjs/queuer.cjs +114 -104
- package/dist/cjs/queuer.cjs.map +1 -1
- package/dist/cjs/queuer.d.cts +53 -40
- package/dist/cjs/rate-limiter.cjs +54 -42
- package/dist/cjs/rate-limiter.cjs.map +1 -1
- package/dist/cjs/rate-limiter.d.cts +37 -45
- package/dist/cjs/throttler.cjs +61 -41
- package/dist/cjs/throttler.cjs.map +1 -1
- package/dist/cjs/throttler.d.cts +35 -22
- package/dist/cjs/types.d.cts +12 -0
- package/dist/cjs/utils.cjs +13 -0
- package/dist/cjs/utils.cjs.map +1 -0
- package/dist/cjs/utils.d.cts +1 -0
- package/dist/esm/async-debouncer.d.ts +37 -24
- package/dist/esm/async-debouncer.js +60 -44
- package/dist/esm/async-debouncer.js.map +1 -1
- package/dist/esm/async-queuer.d.ts +65 -48
- package/dist/esm/async-queuer.js +149 -125
- package/dist/esm/async-queuer.js.map +1 -1
- package/dist/esm/async-rate-limiter.d.ts +39 -27
- package/dist/esm/async-rate-limiter.js +63 -46
- package/dist/esm/async-rate-limiter.js.map +1 -1
- package/dist/esm/async-throttler.d.ts +43 -25
- package/dist/esm/async-throttler.js +70 -47
- package/dist/esm/async-throttler.js.map +1 -1
- package/dist/esm/debouncer.d.ts +25 -11
- package/dist/esm/debouncer.js +46 -22
- package/dist/esm/debouncer.js.map +1 -1
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/queuer.d.ts +53 -40
- package/dist/esm/queuer.js +114 -104
- package/dist/esm/queuer.js.map +1 -1
- package/dist/esm/rate-limiter.d.ts +37 -45
- package/dist/esm/rate-limiter.js +54 -42
- package/dist/esm/rate-limiter.js.map +1 -1
- package/dist/esm/throttler.d.ts +35 -22
- package/dist/esm/throttler.js +61 -41
- package/dist/esm/throttler.js.map +1 -1
- package/dist/esm/types.d.ts +12 -0
- package/dist/esm/utils.d.ts +1 -0
- package/dist/esm/utils.js +13 -0
- package/dist/esm/utils.js.map +1 -0
- package/package.json +8 -1
- package/src/async-debouncer.ts +90 -62
- package/src/async-queuer.ts +178 -145
- package/src/async-rate-limiter.ts +93 -67
- package/src/async-throttler.ts +98 -63
- package/src/debouncer.ts +71 -35
- package/src/index.ts +2 -0
- package/src/queuer.ts +135 -118
- package/src/rate-limiter.ts +79 -81
- package/src/throttler.ts +87 -61
- package/src/types.ts +17 -0
- package/src/utils.ts +13 -0
package/src/throttler.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
import type { AnyFunction } from './types'
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Options for configuring a throttled function
|
|
3
5
|
*/
|
|
4
|
-
export interface ThrottlerOptions
|
|
6
|
+
export interface ThrottlerOptions<
|
|
7
|
+
TFn extends AnyFunction,
|
|
8
|
+
TArgs extends Parameters<TFn>,
|
|
9
|
+
> {
|
|
5
10
|
/**
|
|
6
11
|
* Whether the throttler is enabled. When disabled, maybeExecute will not trigger any executions.
|
|
7
12
|
* Defaults to true.
|
|
@@ -12,6 +17,10 @@ export interface ThrottlerOptions {
|
|
|
12
17
|
* Defaults to true.
|
|
13
18
|
*/
|
|
14
19
|
leading?: boolean
|
|
20
|
+
/**
|
|
21
|
+
* Callback function that is called after the function is executed
|
|
22
|
+
*/
|
|
23
|
+
onExecute?: (throttler: Throttler<TFn, TArgs>) => void
|
|
15
24
|
/**
|
|
16
25
|
* Whether to execute on the trailing edge of the timeout.
|
|
17
26
|
* Defaults to true.
|
|
@@ -23,11 +32,12 @@ export interface ThrottlerOptions {
|
|
|
23
32
|
wait: number
|
|
24
33
|
}
|
|
25
34
|
|
|
26
|
-
const defaultOptions: Required<ThrottlerOptions
|
|
35
|
+
const defaultOptions: Required<ThrottlerOptions<any, any>> = {
|
|
27
36
|
enabled: true,
|
|
28
37
|
leading: true,
|
|
29
38
|
trailing: true,
|
|
30
39
|
wait: 0,
|
|
40
|
+
onExecute: () => {},
|
|
31
41
|
}
|
|
32
42
|
|
|
33
43
|
/**
|
|
@@ -41,8 +51,7 @@ const defaultOptions: Required<ThrottlerOptions> = {
|
|
|
41
51
|
* - Leading: Execute immediately on first call (default: true)
|
|
42
52
|
* - Trailing: Execute after wait period if called during throttle (default: true)
|
|
43
53
|
*
|
|
44
|
-
* For
|
|
45
|
-
* For collapsing rapid-fire events, consider using Debouncer.
|
|
54
|
+
* For collapsing rapid-fire events where you only care about the last call, consider using Debouncer.
|
|
46
55
|
*
|
|
47
56
|
* @example
|
|
48
57
|
* ```ts
|
|
@@ -58,21 +67,19 @@ const defaultOptions: Required<ThrottlerOptions> = {
|
|
|
58
67
|
* throttler.maybeExecute('123'); // Throttled
|
|
59
68
|
* ```
|
|
60
69
|
*/
|
|
61
|
-
export class Throttler<
|
|
62
|
-
|
|
63
|
-
TArgs
|
|
64
|
-
|
|
65
|
-
private
|
|
66
|
-
private
|
|
67
|
-
private
|
|
68
|
-
private options: Required<ThrottlerOptions>
|
|
69
|
-
private timeoutId: NodeJS.Timeout | undefined
|
|
70
|
+
export class Throttler<TFn extends AnyFunction, TArgs extends Parameters<TFn>> {
|
|
71
|
+
private _executionCount = 0
|
|
72
|
+
private _lastArgs: TArgs | undefined
|
|
73
|
+
private _lastExecutionTime = 0
|
|
74
|
+
private _options: Required<ThrottlerOptions<TFn, TArgs>>
|
|
75
|
+
private _timeoutId: NodeJS.Timeout | undefined
|
|
76
|
+
private _isPending = false
|
|
70
77
|
|
|
71
78
|
constructor(
|
|
72
79
|
private fn: TFn,
|
|
73
|
-
initialOptions: ThrottlerOptions,
|
|
80
|
+
initialOptions: ThrottlerOptions<TFn, TArgs>,
|
|
74
81
|
) {
|
|
75
|
-
this.
|
|
82
|
+
this._options = {
|
|
76
83
|
...defaultOptions,
|
|
77
84
|
...initialOptions,
|
|
78
85
|
}
|
|
@@ -83,34 +90,20 @@ export class Throttler<
|
|
|
83
90
|
* Returns the new options state
|
|
84
91
|
*/
|
|
85
92
|
setOptions(
|
|
86
|
-
newOptions: Partial<ThrottlerOptions
|
|
87
|
-
): Required<ThrottlerOptions
|
|
88
|
-
this.
|
|
89
|
-
...this.
|
|
93
|
+
newOptions: Partial<ThrottlerOptions<TFn, TArgs>>,
|
|
94
|
+
): Required<ThrottlerOptions<TFn, TArgs>> {
|
|
95
|
+
this._options = {
|
|
96
|
+
...this._options,
|
|
90
97
|
...newOptions,
|
|
91
98
|
}
|
|
92
|
-
return this.
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Returns the number of times the function has been executed
|
|
97
|
-
*/
|
|
98
|
-
getExecutionCount(): number {
|
|
99
|
-
return this.executionCount
|
|
99
|
+
return this._options
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
/**
|
|
103
|
-
* Returns the
|
|
103
|
+
* Returns the current throttler options
|
|
104
104
|
*/
|
|
105
|
-
|
|
106
|
-
return this.
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Returns the next execution time
|
|
111
|
-
*/
|
|
112
|
-
getNextExecutionTime(): number {
|
|
113
|
-
return this.lastExecutionTime + this.options.wait
|
|
105
|
+
getOptions(): Required<ThrottlerOptions<TFn, TArgs>> {
|
|
106
|
+
return this._options
|
|
114
107
|
}
|
|
115
108
|
|
|
116
109
|
/**
|
|
@@ -137,36 +130,40 @@ export class Throttler<
|
|
|
137
130
|
*/
|
|
138
131
|
maybeExecute(...args: TArgs): void {
|
|
139
132
|
const now = Date.now()
|
|
140
|
-
const timeSinceLastExecution = now - this.
|
|
133
|
+
const timeSinceLastExecution = now - this._lastExecutionTime
|
|
141
134
|
|
|
142
135
|
// Handle leading execution
|
|
143
|
-
if (timeSinceLastExecution >= this.
|
|
144
|
-
if (this.
|
|
136
|
+
if (timeSinceLastExecution >= this._options.wait) {
|
|
137
|
+
if (this._options.leading) {
|
|
145
138
|
this.executeFunction(...args)
|
|
146
139
|
}
|
|
147
|
-
this.
|
|
140
|
+
this._lastExecutionTime = now
|
|
141
|
+
this._isPending = false
|
|
148
142
|
} else {
|
|
149
143
|
// Store the most recent arguments for potential trailing execution
|
|
150
|
-
this.
|
|
144
|
+
this._lastArgs = args
|
|
151
145
|
|
|
152
146
|
// Set up trailing execution if not already scheduled
|
|
153
|
-
if (!this.
|
|
154
|
-
this.
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
this.
|
|
147
|
+
if (!this._timeoutId && this._options.trailing) {
|
|
148
|
+
this._isPending = true
|
|
149
|
+
this._timeoutId = setTimeout(() => {
|
|
150
|
+
if (this._lastArgs) {
|
|
151
|
+
this.executeFunction(...this._lastArgs)
|
|
152
|
+
this._lastArgs = undefined
|
|
158
153
|
}
|
|
159
|
-
this.
|
|
160
|
-
this.
|
|
161
|
-
|
|
154
|
+
this._lastExecutionTime = Date.now()
|
|
155
|
+
this._timeoutId = undefined
|
|
156
|
+
this._isPending = false
|
|
157
|
+
this._options.onExecute(this)
|
|
158
|
+
}, this._options.wait - timeSinceLastExecution)
|
|
162
159
|
}
|
|
163
160
|
}
|
|
164
161
|
}
|
|
165
162
|
|
|
166
163
|
private executeFunction(...args: TArgs): void {
|
|
167
|
-
if (!this.
|
|
168
|
-
this.
|
|
169
|
-
this.
|
|
164
|
+
if (!this._options.enabled) return
|
|
165
|
+
this.fn(...args) // EXECUTE!
|
|
166
|
+
this._executionCount++
|
|
170
167
|
}
|
|
171
168
|
|
|
172
169
|
/**
|
|
@@ -179,12 +176,41 @@ export class Throttler<
|
|
|
179
176
|
* Has no effect if there is no pending execution.
|
|
180
177
|
*/
|
|
181
178
|
cancel(): void {
|
|
182
|
-
if (this.
|
|
183
|
-
clearTimeout(this.
|
|
184
|
-
this.
|
|
185
|
-
this.
|
|
179
|
+
if (this._timeoutId) {
|
|
180
|
+
clearTimeout(this._timeoutId)
|
|
181
|
+
this._timeoutId = undefined
|
|
182
|
+
this._lastArgs = undefined
|
|
183
|
+
this._isPending = false
|
|
186
184
|
}
|
|
187
185
|
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Returns the number of times the function has been executed
|
|
189
|
+
*/
|
|
190
|
+
getExecutionCount(): number {
|
|
191
|
+
return this._executionCount
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Returns `true` if there is a pending execution
|
|
196
|
+
*/
|
|
197
|
+
getIsPending(): boolean {
|
|
198
|
+
return this._options.enabled && this._isPending
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Returns the last execution time
|
|
203
|
+
*/
|
|
204
|
+
getLastExecutionTime(): number {
|
|
205
|
+
return this._lastExecutionTime
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Returns the next execution time
|
|
210
|
+
*/
|
|
211
|
+
getNextExecutionTime(): number {
|
|
212
|
+
return this._lastExecutionTime + this._options.wait
|
|
213
|
+
}
|
|
188
214
|
}
|
|
189
215
|
|
|
190
216
|
/**
|
|
@@ -213,10 +239,10 @@ export class Throttler<
|
|
|
213
239
|
* });
|
|
214
240
|
* ```
|
|
215
241
|
*/
|
|
216
|
-
export function throttle<
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
) {
|
|
242
|
+
export function throttle<
|
|
243
|
+
TFn extends AnyFunction,
|
|
244
|
+
TArgs extends Parameters<TFn>,
|
|
245
|
+
>(fn: TFn, initialOptions: Omit<ThrottlerOptions<TFn, TArgs>, 'enabled'>) {
|
|
220
246
|
const throttler = new Throttler(fn, initialOptions)
|
|
221
247
|
return throttler.maybeExecute.bind(throttler)
|
|
222
248
|
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a function that can be called with any arguments and returns any value.
|
|
3
|
+
* @template TArgs - The type of the arguments the function can be called with.
|
|
4
|
+
* @returns The return value of the function.
|
|
5
|
+
*/
|
|
6
|
+
export type AnyFunction<TArgs extends Array<any> = Array<any>> = (
|
|
7
|
+
...args: TArgs
|
|
8
|
+
) => any
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Represents an asynchronous function that can be called with any arguments and returns a promise.
|
|
12
|
+
* @template TArgs - The type of the arguments the function can be called with.
|
|
13
|
+
* @returns A promise that resolves to the return value of the function.
|
|
14
|
+
*/
|
|
15
|
+
export type AnyAsyncFunction<TArgs extends Array<any> = Array<any>> = (
|
|
16
|
+
...args: TArgs
|
|
17
|
+
) => Promise<any>
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function bindInstanceMethods<T extends Record<string, any>>(
|
|
2
|
+
instance: T,
|
|
3
|
+
) {
|
|
4
|
+
return Object.getOwnPropertyNames(Object.getPrototypeOf(instance))
|
|
5
|
+
.filter((key) => typeof instance[key as keyof T] === 'function')
|
|
6
|
+
.reduce((acc: any, key) => {
|
|
7
|
+
const method = instance[key as keyof T]
|
|
8
|
+
if (typeof method === 'function') {
|
|
9
|
+
acc[key] = method.bind(instance)
|
|
10
|
+
}
|
|
11
|
+
return acc
|
|
12
|
+
}, {} as T)
|
|
13
|
+
}
|