@tanstack/pacer 0.1.0 → 0.3.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 +112 -63
- package/dist/cjs/async-debouncer.cjs.map +1 -1
- package/dist/cjs/async-debouncer.d.cts +66 -25
- package/dist/cjs/async-queuer.cjs +198 -124
- package/dist/cjs/async-queuer.cjs.map +1 -1
- package/dist/cjs/async-queuer.d.cts +91 -49
- package/dist/cjs/async-rate-limiter.cjs +83 -55
- package/dist/cjs/async-rate-limiter.cjs.map +1 -1
- package/dist/cjs/async-rate-limiter.d.cts +55 -28
- package/dist/cjs/async-throttler.cjs +121 -70
- package/dist/cjs/async-throttler.cjs.map +1 -1
- package/dist/cjs/async-throttler.d.cts +75 -25
- package/dist/cjs/debouncer.cjs +45 -23
- package/dist/cjs/debouncer.cjs.map +1 -1
- package/dist/cjs/debouncer.d.cts +27 -12
- 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 +161 -101
- package/dist/cjs/queuer.cjs.map +1 -1
- package/dist/cjs/queuer.d.cts +80 -38
- package/dist/cjs/rate-limiter.cjs +52 -44
- package/dist/cjs/rate-limiter.cjs.map +1 -1
- package/dist/cjs/rate-limiter.d.cts +38 -46
- package/dist/cjs/throttler.cjs +57 -44
- package/dist/cjs/throttler.cjs.map +1 -1
- package/dist/cjs/throttler.d.cts +35 -23
- package/dist/cjs/types.d.cts +8 -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 +66 -25
- package/dist/esm/async-debouncer.js +112 -63
- package/dist/esm/async-debouncer.js.map +1 -1
- package/dist/esm/async-queuer.d.ts +91 -49
- package/dist/esm/async-queuer.js +198 -124
- package/dist/esm/async-queuer.js.map +1 -1
- package/dist/esm/async-rate-limiter.d.ts +55 -28
- package/dist/esm/async-rate-limiter.js +83 -55
- package/dist/esm/async-rate-limiter.js.map +1 -1
- package/dist/esm/async-throttler.d.ts +75 -25
- package/dist/esm/async-throttler.js +121 -70
- package/dist/esm/async-throttler.js.map +1 -1
- package/dist/esm/debouncer.d.ts +27 -12
- package/dist/esm/debouncer.js +45 -23
- 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 +80 -38
- package/dist/esm/queuer.js +161 -101
- package/dist/esm/queuer.js.map +1 -1
- package/dist/esm/rate-limiter.d.ts +38 -46
- package/dist/esm/rate-limiter.js +52 -44
- package/dist/esm/rate-limiter.js.map +1 -1
- package/dist/esm/throttler.d.ts +35 -23
- package/dist/esm/throttler.js +57 -44
- package/dist/esm/throttler.js.map +1 -1
- package/dist/esm/types.d.ts +8 -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 +157 -88
- package/src/async-queuer.ts +266 -148
- package/src/async-rate-limiter.ts +123 -83
- package/src/async-throttler.ts +173 -89
- package/src/debouncer.ts +71 -42
- package/src/index.ts +2 -0
- package/src/queuer.ts +219 -114
- package/src/rate-limiter.ts +74 -88
- package/src/throttler.ts +83 -65
- package/src/types.ts +9 -0
- package/src/utils.ts +13 -0
package/src/rate-limiter.ts
CHANGED
|
@@ -1,29 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
* Information about a rate limit rejection
|
|
3
|
-
*/
|
|
4
|
-
export interface RateLimitRejectionInfo {
|
|
5
|
-
/**
|
|
6
|
-
* Number of milliseconds until the next execution will be possible
|
|
7
|
-
*/
|
|
8
|
-
msUntilNextWindow: number
|
|
9
|
-
/**
|
|
10
|
-
* Current number of executions in the window
|
|
11
|
-
*/
|
|
12
|
-
currentExecutions: number
|
|
13
|
-
/**
|
|
14
|
-
* Maximum allowed executions per window
|
|
15
|
-
*/
|
|
16
|
-
limit: number
|
|
17
|
-
/**
|
|
18
|
-
* Total number of rejections that have occurred
|
|
19
|
-
*/
|
|
20
|
-
rejectionCount: number
|
|
21
|
-
}
|
|
1
|
+
import type { AnyFunction } from './types'
|
|
22
2
|
|
|
23
3
|
/**
|
|
24
4
|
* Options for configuring a rate-limited function
|
|
25
5
|
*/
|
|
26
|
-
export interface RateLimiterOptions {
|
|
6
|
+
export interface RateLimiterOptions<TFn extends AnyFunction> {
|
|
27
7
|
/**
|
|
28
8
|
* Whether the rate limiter is enabled. When disabled, maybeExecute will not trigger any executions.
|
|
29
9
|
* Defaults to true.
|
|
@@ -34,18 +14,24 @@ export interface RateLimiterOptions {
|
|
|
34
14
|
*/
|
|
35
15
|
limit: number
|
|
36
16
|
/**
|
|
37
|
-
*
|
|
17
|
+
* Callback function that is called after the function is executed
|
|
38
18
|
*/
|
|
39
|
-
|
|
19
|
+
onExecute?: (rateLimiter: RateLimiter<TFn>) => void
|
|
40
20
|
/**
|
|
41
21
|
* Optional callback function that is called when an execution is rejected due to rate limiting
|
|
42
22
|
*/
|
|
43
|
-
onReject?: (
|
|
23
|
+
onReject?: (rateLimiter: RateLimiter<TFn>) => void
|
|
24
|
+
/**
|
|
25
|
+
* Time window in milliseconds within which the limit applies
|
|
26
|
+
*/
|
|
27
|
+
window: number
|
|
44
28
|
}
|
|
45
29
|
|
|
46
|
-
const defaultOptions: Required<
|
|
30
|
+
const defaultOptions: Required<RateLimiterOptions<any>> = {
|
|
47
31
|
enabled: true,
|
|
48
32
|
limit: 1,
|
|
33
|
+
onExecute: () => {},
|
|
34
|
+
onReject: () => {},
|
|
49
35
|
window: 0,
|
|
50
36
|
}
|
|
51
37
|
|
|
@@ -74,20 +60,17 @@ const defaultOptions: Required<Omit<RateLimiterOptions, 'onReject'>> = {
|
|
|
74
60
|
* rateLimiter.maybeExecute('123');
|
|
75
61
|
* ```
|
|
76
62
|
*/
|
|
77
|
-
export class RateLimiter<
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
>
|
|
81
|
-
private
|
|
82
|
-
private rejectionCount = 0
|
|
83
|
-
private executionTimes: Array<number> = []
|
|
84
|
-
private options: RateLimiterOptions
|
|
63
|
+
export class RateLimiter<TFn extends AnyFunction> {
|
|
64
|
+
private _executionCount = 0
|
|
65
|
+
private _rejectionCount = 0
|
|
66
|
+
private _executionTimes: Array<number> = []
|
|
67
|
+
private _options: RateLimiterOptions<TFn>
|
|
85
68
|
|
|
86
69
|
constructor(
|
|
87
70
|
private fn: TFn,
|
|
88
|
-
initialOptions: RateLimiterOptions
|
|
71
|
+
initialOptions: RateLimiterOptions<TFn>,
|
|
89
72
|
) {
|
|
90
|
-
this.
|
|
73
|
+
this._options = {
|
|
91
74
|
...defaultOptions,
|
|
92
75
|
...initialOptions,
|
|
93
76
|
}
|
|
@@ -97,34 +80,15 @@ export class RateLimiter<
|
|
|
97
80
|
* Updates the rate limiter options
|
|
98
81
|
* Returns the new options state
|
|
99
82
|
*/
|
|
100
|
-
setOptions(newOptions: Partial<RateLimiterOptions
|
|
101
|
-
this.
|
|
102
|
-
...this.options,
|
|
103
|
-
...newOptions,
|
|
104
|
-
}
|
|
105
|
-
return this.options
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Returns the number of times the function has been executed
|
|
110
|
-
*/
|
|
111
|
-
getExecutionCount(): number {
|
|
112
|
-
return this.executionCount
|
|
83
|
+
setOptions(newOptions: Partial<RateLimiterOptions<TFn>>): void {
|
|
84
|
+
this._options = { ...this._options, ...newOptions }
|
|
113
85
|
}
|
|
114
86
|
|
|
115
87
|
/**
|
|
116
|
-
* Returns the
|
|
88
|
+
* Returns the current rate limiter options
|
|
117
89
|
*/
|
|
118
|
-
|
|
119
|
-
return this.
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Returns the number of remaining executions allowed in the current window
|
|
124
|
-
*/
|
|
125
|
-
getRemainingInWindow(): number {
|
|
126
|
-
this.cleanupOldExecutions()
|
|
127
|
-
return Math.max(0, this.options.limit - this.executionTimes.length)
|
|
90
|
+
getOptions(): Required<RateLimiterOptions<TFn>> {
|
|
91
|
+
return this._options as Required<RateLimiterOptions<TFn>>
|
|
128
92
|
}
|
|
129
93
|
|
|
130
94
|
/**
|
|
@@ -142,10 +106,10 @@ export class RateLimiter<
|
|
|
142
106
|
* rateLimiter.maybeExecute('arg1', 'arg2'); // false
|
|
143
107
|
* ```
|
|
144
108
|
*/
|
|
145
|
-
maybeExecute(...args:
|
|
109
|
+
maybeExecute(...args: Parameters<TFn>): boolean {
|
|
146
110
|
this.cleanupOldExecutions()
|
|
147
111
|
|
|
148
|
-
if (this.
|
|
112
|
+
if (this._executionTimes.length < this._options.limit) {
|
|
149
113
|
this.executeFunction(...args)
|
|
150
114
|
return true
|
|
151
115
|
}
|
|
@@ -155,45 +119,67 @@ export class RateLimiter<
|
|
|
155
119
|
return false
|
|
156
120
|
}
|
|
157
121
|
|
|
158
|
-
private executeFunction(...args:
|
|
159
|
-
if (!this.
|
|
122
|
+
private executeFunction(...args: Parameters<TFn>): void {
|
|
123
|
+
if (!this._options.enabled) return
|
|
160
124
|
const now = Date.now()
|
|
161
|
-
this.
|
|
162
|
-
this.
|
|
163
|
-
this.fn(...args)
|
|
125
|
+
this._executionCount++
|
|
126
|
+
this._executionTimes.push(now)
|
|
127
|
+
this.fn(...args) // execute the function
|
|
128
|
+
this._options.onExecute?.(this)
|
|
164
129
|
}
|
|
165
130
|
|
|
166
131
|
private rejectFunction(): void {
|
|
167
|
-
this.
|
|
168
|
-
if (this.
|
|
169
|
-
|
|
170
|
-
const msUntilNextWindow =
|
|
171
|
-
oldestExecution + this.options.window - Date.now()
|
|
172
|
-
|
|
173
|
-
this.options.onReject({
|
|
174
|
-
msUntilNextWindow,
|
|
175
|
-
currentExecutions: this.executionTimes.length,
|
|
176
|
-
limit: this.options.limit,
|
|
177
|
-
rejectionCount: this.rejectionCount,
|
|
178
|
-
})
|
|
132
|
+
this._rejectionCount++
|
|
133
|
+
if (this._options.onReject) {
|
|
134
|
+
this._options.onReject(this)
|
|
179
135
|
}
|
|
180
136
|
}
|
|
181
137
|
|
|
182
138
|
private cleanupOldExecutions(): void {
|
|
183
139
|
const now = Date.now()
|
|
184
|
-
const windowStart = now - this.
|
|
185
|
-
this.
|
|
140
|
+
const windowStart = now - this._options.window
|
|
141
|
+
this._executionTimes = this._executionTimes.filter(
|
|
186
142
|
(time) => time > windowStart,
|
|
187
143
|
)
|
|
188
144
|
}
|
|
189
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Returns the number of times the function has been executed
|
|
148
|
+
*/
|
|
149
|
+
getExecutionCount(): number {
|
|
150
|
+
return this._executionCount
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Returns the number of times the function has been rejected
|
|
155
|
+
*/
|
|
156
|
+
getRejectionCount(): number {
|
|
157
|
+
return this._rejectionCount
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Returns the number of remaining executions allowed in the current window
|
|
162
|
+
*/
|
|
163
|
+
getRemainingInWindow(): number {
|
|
164
|
+
this.cleanupOldExecutions()
|
|
165
|
+
return Math.max(0, this._options.limit - this._executionTimes.length)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Returns the number of milliseconds until the next execution will be possible
|
|
170
|
+
*/
|
|
171
|
+
getMsUntilNextWindow(): number {
|
|
172
|
+
const oldestExecution = Math.min(...this._executionTimes)
|
|
173
|
+
return oldestExecution + this._options.window - Date.now()
|
|
174
|
+
}
|
|
175
|
+
|
|
190
176
|
/**
|
|
191
177
|
* Resets the rate limiter state
|
|
192
178
|
*/
|
|
193
179
|
reset(): void {
|
|
194
|
-
this.
|
|
195
|
-
this.
|
|
196
|
-
this.
|
|
180
|
+
this._executionTimes = []
|
|
181
|
+
this._executionCount = 0
|
|
182
|
+
this._rejectionCount = 0
|
|
197
183
|
}
|
|
198
184
|
}
|
|
199
185
|
|
|
@@ -214,8 +200,8 @@ export class RateLimiter<
|
|
|
214
200
|
* const rateLimited = rateLimit(makeApiCall, {
|
|
215
201
|
* limit: 5,
|
|
216
202
|
* window: 60000,
|
|
217
|
-
* onReject: (
|
|
218
|
-
* console.log(`Rate limit exceeded. Try again in ${
|
|
203
|
+
* onReject: (rateLimiter) => {
|
|
204
|
+
* console.log(`Rate limit exceeded. Try again in ${rateLimiter.getMsUntilNextWindow()}ms`);
|
|
219
205
|
* }
|
|
220
206
|
* });
|
|
221
207
|
*
|
|
@@ -227,9 +213,9 @@ export class RateLimiter<
|
|
|
227
213
|
* const throttled = throttle(makeApiCall, { wait: 12000 }); // One call every 12 seconds
|
|
228
214
|
* ```
|
|
229
215
|
*/
|
|
230
|
-
export function rateLimit<TFn extends
|
|
216
|
+
export function rateLimit<TFn extends AnyFunction>(
|
|
231
217
|
fn: TFn,
|
|
232
|
-
initialOptions: Omit<RateLimiterOptions
|
|
218
|
+
initialOptions: Omit<RateLimiterOptions<TFn>, 'enabled'>,
|
|
233
219
|
) {
|
|
234
220
|
const rateLimiter = new RateLimiter(fn, initialOptions)
|
|
235
221
|
return rateLimiter.maybeExecute.bind(rateLimiter)
|
package/src/throttler.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
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<TFn extends AnyFunction> {
|
|
5
7
|
/**
|
|
6
8
|
* Whether the throttler is enabled. When disabled, maybeExecute will not trigger any executions.
|
|
7
9
|
* Defaults to true.
|
|
@@ -12,6 +14,10 @@ export interface ThrottlerOptions {
|
|
|
12
14
|
* Defaults to true.
|
|
13
15
|
*/
|
|
14
16
|
leading?: boolean
|
|
17
|
+
/**
|
|
18
|
+
* Callback function that is called after the function is executed
|
|
19
|
+
*/
|
|
20
|
+
onExecute?: (throttler: Throttler<TFn>) => void
|
|
15
21
|
/**
|
|
16
22
|
* Whether to execute on the trailing edge of the timeout.
|
|
17
23
|
* Defaults to true.
|
|
@@ -23,9 +29,10 @@ export interface ThrottlerOptions {
|
|
|
23
29
|
wait: number
|
|
24
30
|
}
|
|
25
31
|
|
|
26
|
-
const defaultOptions: Required<ThrottlerOptions
|
|
32
|
+
const defaultOptions: Required<ThrottlerOptions<any>> = {
|
|
27
33
|
enabled: true,
|
|
28
34
|
leading: true,
|
|
35
|
+
onExecute: () => {},
|
|
29
36
|
trailing: true,
|
|
30
37
|
wait: 0,
|
|
31
38
|
}
|
|
@@ -41,8 +48,7 @@ const defaultOptions: Required<ThrottlerOptions> = {
|
|
|
41
48
|
* - Leading: Execute immediately on first call (default: true)
|
|
42
49
|
* - Trailing: Execute after wait period if called during throttle (default: true)
|
|
43
50
|
*
|
|
44
|
-
* For
|
|
45
|
-
* For collapsing rapid-fire events, consider using Debouncer.
|
|
51
|
+
* For collapsing rapid-fire events where you only care about the last call, consider using Debouncer.
|
|
46
52
|
*
|
|
47
53
|
* @example
|
|
48
54
|
* ```ts
|
|
@@ -58,21 +64,18 @@ const defaultOptions: Required<ThrottlerOptions> = {
|
|
|
58
64
|
* throttler.maybeExecute('123'); // Throttled
|
|
59
65
|
* ```
|
|
60
66
|
*/
|
|
61
|
-
export class Throttler<
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
private
|
|
66
|
-
private
|
|
67
|
-
private lastExecutionTime = 0
|
|
68
|
-
private options: Required<ThrottlerOptions>
|
|
69
|
-
private timeoutId: NodeJS.Timeout | undefined
|
|
67
|
+
export class Throttler<TFn extends AnyFunction> {
|
|
68
|
+
private _executionCount = 0
|
|
69
|
+
private _lastArgs: Parameters<TFn> | undefined
|
|
70
|
+
private _lastExecutionTime = 0
|
|
71
|
+
private _options: Required<ThrottlerOptions<TFn>>
|
|
72
|
+
private _timeoutId: NodeJS.Timeout | undefined
|
|
70
73
|
|
|
71
74
|
constructor(
|
|
72
75
|
private fn: TFn,
|
|
73
|
-
initialOptions: ThrottlerOptions
|
|
76
|
+
initialOptions: ThrottlerOptions<TFn>,
|
|
74
77
|
) {
|
|
75
|
-
this.
|
|
78
|
+
this._options = {
|
|
76
79
|
...defaultOptions,
|
|
77
80
|
...initialOptions,
|
|
78
81
|
}
|
|
@@ -82,35 +85,20 @@ export class Throttler<
|
|
|
82
85
|
* Updates the throttler options
|
|
83
86
|
* Returns the new options state
|
|
84
87
|
*/
|
|
85
|
-
setOptions(
|
|
86
|
-
newOptions
|
|
87
|
-
): Required<ThrottlerOptions> {
|
|
88
|
-
this.options = {
|
|
89
|
-
...this.options,
|
|
90
|
-
...newOptions,
|
|
91
|
-
}
|
|
92
|
-
return this.options
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Returns the number of times the function has been executed
|
|
97
|
-
*/
|
|
98
|
-
getExecutionCount(): number {
|
|
99
|
-
return this.executionCount
|
|
100
|
-
}
|
|
88
|
+
setOptions(newOptions: Partial<ThrottlerOptions<TFn>>): void {
|
|
89
|
+
this._options = { ...this._options, ...newOptions }
|
|
101
90
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
return this.lastExecutionTime
|
|
91
|
+
// End the pending state if the debouncer is disabled
|
|
92
|
+
if (!this._options.enabled) {
|
|
93
|
+
this.cancel()
|
|
94
|
+
}
|
|
107
95
|
}
|
|
108
96
|
|
|
109
97
|
/**
|
|
110
|
-
* Returns the
|
|
98
|
+
* Returns the current throttler options
|
|
111
99
|
*/
|
|
112
|
-
|
|
113
|
-
return this.
|
|
100
|
+
getOptions(): Required<ThrottlerOptions<TFn>> {
|
|
101
|
+
return this._options
|
|
114
102
|
}
|
|
115
103
|
|
|
116
104
|
/**
|
|
@@ -135,38 +123,40 @@ export class Throttler<
|
|
|
135
123
|
* throttled.maybeExecute('c', 'd');
|
|
136
124
|
* ```
|
|
137
125
|
*/
|
|
138
|
-
maybeExecute(...args:
|
|
126
|
+
maybeExecute(...args: Parameters<TFn>): void {
|
|
139
127
|
const now = Date.now()
|
|
140
|
-
const timeSinceLastExecution = now - this.
|
|
128
|
+
const timeSinceLastExecution = now - this._lastExecutionTime
|
|
141
129
|
|
|
142
130
|
// Handle leading execution
|
|
143
|
-
if (timeSinceLastExecution >= this.
|
|
144
|
-
|
|
145
|
-
this.executeFunction(...args)
|
|
146
|
-
}
|
|
147
|
-
this.lastExecutionTime = now
|
|
131
|
+
if (this._options.leading && timeSinceLastExecution >= this._options.wait) {
|
|
132
|
+
this.executeFunction(...args)
|
|
148
133
|
} else {
|
|
149
134
|
// Store the most recent arguments for potential trailing execution
|
|
150
|
-
this.
|
|
135
|
+
this._lastArgs = args
|
|
151
136
|
|
|
152
137
|
// Set up trailing execution if not already scheduled
|
|
153
|
-
if (!this.
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
138
|
+
if (!this._timeoutId && this._options.trailing) {
|
|
139
|
+
const _timeSinceLastExecution = this._lastExecutionTime
|
|
140
|
+
? now - this._lastExecutionTime
|
|
141
|
+
: 0
|
|
142
|
+
const timeoutDuration = this._options.wait - _timeSinceLastExecution
|
|
143
|
+
this._timeoutId = setTimeout(() => {
|
|
144
|
+
if (this._lastArgs !== undefined) {
|
|
145
|
+
this.executeFunction(...this._lastArgs)
|
|
158
146
|
}
|
|
159
|
-
|
|
160
|
-
this.timeoutId = undefined
|
|
161
|
-
}, this.options.wait - timeSinceLastExecution)
|
|
147
|
+
}, timeoutDuration)
|
|
162
148
|
}
|
|
163
149
|
}
|
|
164
150
|
}
|
|
165
151
|
|
|
166
|
-
private executeFunction(...args:
|
|
167
|
-
if (!this.
|
|
168
|
-
this.
|
|
169
|
-
this.
|
|
152
|
+
private executeFunction(...args: Parameters<TFn>): void {
|
|
153
|
+
if (!this._options.enabled) return
|
|
154
|
+
this.fn(...args) // EXECUTE!
|
|
155
|
+
this._executionCount++
|
|
156
|
+
this._lastExecutionTime = Date.now()
|
|
157
|
+
this._timeoutId = undefined
|
|
158
|
+
this._lastArgs = undefined
|
|
159
|
+
this._options.onExecute(this)
|
|
170
160
|
}
|
|
171
161
|
|
|
172
162
|
/**
|
|
@@ -179,12 +169,40 @@ export class Throttler<
|
|
|
179
169
|
* Has no effect if there is no pending execution.
|
|
180
170
|
*/
|
|
181
171
|
cancel(): void {
|
|
182
|
-
if (this.
|
|
183
|
-
clearTimeout(this.
|
|
184
|
-
this.
|
|
185
|
-
this.
|
|
172
|
+
if (this._timeoutId) {
|
|
173
|
+
clearTimeout(this._timeoutId)
|
|
174
|
+
this._timeoutId = undefined
|
|
175
|
+
this._lastArgs = undefined
|
|
186
176
|
}
|
|
187
177
|
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Returns the last execution time
|
|
181
|
+
*/
|
|
182
|
+
getLastExecutionTime(): number {
|
|
183
|
+
return this._lastExecutionTime
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Returns the next execution time
|
|
188
|
+
*/
|
|
189
|
+
getNextExecutionTime(): number {
|
|
190
|
+
return this._lastExecutionTime + this._options.wait
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Returns the number of times the function has been executed
|
|
195
|
+
*/
|
|
196
|
+
getExecutionCount(): number {
|
|
197
|
+
return this._executionCount
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Returns `true` if there is a pending execution
|
|
202
|
+
*/
|
|
203
|
+
getIsPending(): boolean {
|
|
204
|
+
return this._options.enabled && !!this._timeoutId
|
|
205
|
+
}
|
|
188
206
|
}
|
|
189
207
|
|
|
190
208
|
/**
|
|
@@ -213,9 +231,9 @@ export class Throttler<
|
|
|
213
231
|
* });
|
|
214
232
|
* ```
|
|
215
233
|
*/
|
|
216
|
-
export function throttle<TFn extends
|
|
234
|
+
export function throttle<TFn extends AnyFunction>(
|
|
217
235
|
fn: TFn,
|
|
218
|
-
initialOptions: Omit<ThrottlerOptions
|
|
236
|
+
initialOptions: Omit<ThrottlerOptions<TFn>, 'enabled'>,
|
|
219
237
|
) {
|
|
220
238
|
const throttler = new Throttler(fn, initialOptions)
|
|
221
239
|
return throttler.maybeExecute.bind(throttler)
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a function that can be called with any arguments and returns any value.
|
|
3
|
+
*/
|
|
4
|
+
export type AnyFunction = (...args: Array<any>) => any
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Represents an asynchronous function that can be called with any arguments and returns a promise.
|
|
8
|
+
*/
|
|
9
|
+
export type AnyAsyncFunction = (...args: Array<any>) => Promise<any>
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function bindInstanceMethods<T extends Record<string, any>>(
|
|
2
|
+
instance: T,
|
|
3
|
+
): T {
|
|
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
|
+
}
|