@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/dist/esm/async-queuer.js
CHANGED
|
@@ -2,31 +2,36 @@ const defaultOptions = {
|
|
|
2
2
|
addItemsTo: "back",
|
|
3
3
|
concurrency: 1,
|
|
4
4
|
getItemsFrom: "front",
|
|
5
|
-
getPriority: (item) => item.priority ?? 0,
|
|
5
|
+
getPriority: (item) => (item == null ? void 0 : item.priority) ?? 0,
|
|
6
6
|
initialItems: [],
|
|
7
7
|
maxSize: Infinity,
|
|
8
8
|
onGetNextItem: () => {
|
|
9
9
|
},
|
|
10
|
-
|
|
10
|
+
onIsRunningChange: () => {
|
|
11
|
+
},
|
|
12
|
+
onItemsChange: () => {
|
|
13
|
+
},
|
|
14
|
+
onReject: () => {
|
|
11
15
|
},
|
|
12
16
|
started: false,
|
|
13
17
|
wait: 0
|
|
14
18
|
};
|
|
15
19
|
class AsyncQueuer {
|
|
16
20
|
constructor(initialOptions = defaultOptions) {
|
|
17
|
-
this.
|
|
18
|
-
this.
|
|
19
|
-
this.
|
|
20
|
-
this.
|
|
21
|
-
this.
|
|
22
|
-
this.
|
|
23
|
-
this.
|
|
24
|
-
this.
|
|
25
|
-
this.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const
|
|
29
|
-
|
|
21
|
+
this._activeItems = /* @__PURE__ */ new Set();
|
|
22
|
+
this._executionCount = 0;
|
|
23
|
+
this._rejectionCount = 0;
|
|
24
|
+
this._items = [];
|
|
25
|
+
this._onErrorCallbacks = [];
|
|
26
|
+
this._onSettledCallbacks = [];
|
|
27
|
+
this._onSuccessCallbacks = [];
|
|
28
|
+
this._pendingTick = false;
|
|
29
|
+
this._options = { ...defaultOptions, ...initialOptions };
|
|
30
|
+
this._running = this._options.started;
|
|
31
|
+
for (let i = 0; i < this._options.initialItems.length; i++) {
|
|
32
|
+
const item = this._options.initialItems[i];
|
|
33
|
+
const isLast = i === this._options.initialItems.length - 1;
|
|
34
|
+
this.addItem(item, this._options.addItemsTo, isLast);
|
|
30
35
|
}
|
|
31
36
|
}
|
|
32
37
|
/**
|
|
@@ -34,23 +39,30 @@ class AsyncQueuer {
|
|
|
34
39
|
* Returns the new options state
|
|
35
40
|
*/
|
|
36
41
|
setOptions(newOptions) {
|
|
37
|
-
this.
|
|
38
|
-
return this.
|
|
42
|
+
this._options = { ...this._options, ...newOptions };
|
|
43
|
+
return this._options;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Returns the current queuer options
|
|
47
|
+
*/
|
|
48
|
+
getOptions() {
|
|
49
|
+
return this._options;
|
|
39
50
|
}
|
|
40
51
|
/**
|
|
41
52
|
* Processes items in the queuer
|
|
42
53
|
*/
|
|
43
54
|
tick() {
|
|
44
|
-
if (!this.
|
|
45
|
-
this.
|
|
55
|
+
if (!this._running) {
|
|
56
|
+
this._pendingTick = false;
|
|
46
57
|
return;
|
|
47
58
|
}
|
|
48
|
-
while (this.
|
|
59
|
+
while (this._activeItems.size < this._options.concurrency && !this.getIsEmpty()) {
|
|
49
60
|
const nextFn = this.getNextItem();
|
|
50
61
|
if (!nextFn) {
|
|
51
62
|
break;
|
|
52
63
|
}
|
|
53
|
-
this.
|
|
64
|
+
this._activeItems.add(nextFn);
|
|
65
|
+
this._options.onItemsChange(this);
|
|
54
66
|
(async () => {
|
|
55
67
|
let success = false;
|
|
56
68
|
let res;
|
|
@@ -61,29 +73,78 @@ class AsyncQueuer {
|
|
|
61
73
|
} catch (e) {
|
|
62
74
|
error = e;
|
|
63
75
|
} finally {
|
|
64
|
-
this.
|
|
76
|
+
this._activeItems.delete(nextFn);
|
|
77
|
+
this._options.onItemsChange(this);
|
|
65
78
|
}
|
|
66
|
-
this.activeItems.delete(nextFn);
|
|
67
79
|
if (success) {
|
|
68
|
-
this.
|
|
80
|
+
this._onSuccessCallbacks.forEach((cb) => cb(res));
|
|
69
81
|
} else {
|
|
70
|
-
this.
|
|
82
|
+
this._onErrorCallbacks.forEach((cb) => cb(error));
|
|
71
83
|
}
|
|
72
|
-
this.
|
|
73
|
-
if (this.
|
|
74
|
-
setTimeout(() => this.tick(), this.
|
|
84
|
+
this._onSettledCallbacks.forEach((cb) => cb(success ? res : error));
|
|
85
|
+
if (this._options.wait > 0) {
|
|
86
|
+
setTimeout(() => this.tick(), this._options.wait);
|
|
75
87
|
return;
|
|
76
88
|
}
|
|
77
89
|
this.tick();
|
|
78
90
|
})();
|
|
79
91
|
}
|
|
80
|
-
this.
|
|
92
|
+
this._pendingTick = false;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Starts the queuer and processes items
|
|
96
|
+
*/
|
|
97
|
+
start() {
|
|
98
|
+
this._running = true;
|
|
99
|
+
if (!this._pendingTick && !this.getIsEmpty()) {
|
|
100
|
+
this._pendingTick = true;
|
|
101
|
+
this.tick();
|
|
102
|
+
}
|
|
103
|
+
this._options.onIsRunningChange(this);
|
|
104
|
+
return new Promise((resolve) => {
|
|
105
|
+
const checkIdle = () => {
|
|
106
|
+
if (this.getIsIdle()) {
|
|
107
|
+
resolve();
|
|
108
|
+
} else {
|
|
109
|
+
setTimeout(checkIdle, 100);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
checkIdle();
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Stops the queuer from processing items
|
|
117
|
+
*/
|
|
118
|
+
stop() {
|
|
119
|
+
this._running = false;
|
|
120
|
+
this._pendingTick = false;
|
|
121
|
+
this._options.onIsRunningChange(this);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Removes all items from the queuer
|
|
125
|
+
*/
|
|
126
|
+
clear() {
|
|
127
|
+
this._items = [];
|
|
128
|
+
this._options.onItemsChange(this);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Resets the queuer to its initial state
|
|
132
|
+
*/
|
|
133
|
+
reset(withInitialItems) {
|
|
134
|
+
this.clear();
|
|
135
|
+
this._executionCount = 0;
|
|
136
|
+
if (withInitialItems) {
|
|
137
|
+
this._items = [...this._options.initialItems];
|
|
138
|
+
}
|
|
139
|
+
this._running = this._options.started;
|
|
81
140
|
}
|
|
82
141
|
/**
|
|
83
142
|
* Adds a task to the queuer
|
|
84
143
|
*/
|
|
85
|
-
addItem(fn, position = this.
|
|
86
|
-
if (this.
|
|
144
|
+
addItem(fn, position = this._options.addItemsTo, runOnUpdate = true) {
|
|
145
|
+
if (this.getIsFull()) {
|
|
146
|
+
this._rejectionCount++;
|
|
147
|
+
this._options.onReject(fn, this);
|
|
87
148
|
return Promise.reject(new Error("Queuer is full"));
|
|
88
149
|
}
|
|
89
150
|
return new Promise((resolve, reject) => {
|
|
@@ -100,29 +161,29 @@ class AsyncQueuer {
|
|
|
100
161
|
},
|
|
101
162
|
{ priority: fn.priority ?? void 0 }
|
|
102
163
|
);
|
|
103
|
-
const priority = this.
|
|
164
|
+
const priority = this._options.getPriority !== defaultOptions.getPriority ? this._options.getPriority(task) : task.priority;
|
|
104
165
|
if (priority !== void 0) {
|
|
105
|
-
const insertIndex = this.
|
|
106
|
-
const existingPriority = this.
|
|
166
|
+
const insertIndex = this._items.findIndex((existing) => {
|
|
167
|
+
const existingPriority = this._options.getPriority !== defaultOptions.getPriority ? this._options.getPriority(existing) : existing.priority;
|
|
107
168
|
return existingPriority > priority;
|
|
108
169
|
});
|
|
109
170
|
if (insertIndex === -1) {
|
|
110
|
-
this.
|
|
171
|
+
this._items.push(task);
|
|
111
172
|
} else {
|
|
112
|
-
this.
|
|
173
|
+
this._items.splice(insertIndex, 0, task);
|
|
113
174
|
}
|
|
114
175
|
} else {
|
|
115
176
|
if (position === "front") {
|
|
116
|
-
this.
|
|
177
|
+
this._items.unshift(task);
|
|
117
178
|
} else {
|
|
118
|
-
this.
|
|
179
|
+
this._items.push(task);
|
|
119
180
|
}
|
|
120
181
|
}
|
|
121
182
|
if (runOnUpdate) {
|
|
122
|
-
this.
|
|
183
|
+
this._options.onItemsChange(this);
|
|
123
184
|
}
|
|
124
|
-
if (this.
|
|
125
|
-
this.
|
|
185
|
+
if (this._running && !this._pendingTick) {
|
|
186
|
+
this._pendingTick = true;
|
|
126
187
|
this.tick();
|
|
127
188
|
}
|
|
128
189
|
});
|
|
@@ -130,157 +191,120 @@ class AsyncQueuer {
|
|
|
130
191
|
/**
|
|
131
192
|
* Removes and returns an item from the queuer
|
|
132
193
|
*/
|
|
133
|
-
getNextItem(position = this.
|
|
194
|
+
getNextItem(position = this._options.getItemsFrom) {
|
|
134
195
|
let item;
|
|
135
196
|
if (position === "front") {
|
|
136
|
-
item = this.
|
|
197
|
+
item = this._items.shift();
|
|
137
198
|
} else {
|
|
138
|
-
item = this.
|
|
199
|
+
item = this._items.pop();
|
|
139
200
|
}
|
|
140
201
|
if (item !== void 0) {
|
|
141
|
-
this.
|
|
142
|
-
this.
|
|
143
|
-
this.
|
|
202
|
+
this._executionCount++;
|
|
203
|
+
this._options.onItemsChange(this);
|
|
204
|
+
this._options.onGetNextItem(item, this);
|
|
144
205
|
}
|
|
145
206
|
return item;
|
|
146
207
|
}
|
|
147
208
|
/**
|
|
148
209
|
* Returns an item without removing it
|
|
149
210
|
*/
|
|
150
|
-
|
|
211
|
+
getPeek(position = "front") {
|
|
151
212
|
if (position === "front") {
|
|
152
|
-
return this.
|
|
213
|
+
return this._items[0];
|
|
153
214
|
}
|
|
154
|
-
return this.
|
|
215
|
+
return this._items[this._items.length - 1];
|
|
155
216
|
}
|
|
156
217
|
/**
|
|
157
218
|
* Returns true if the queuer is empty
|
|
158
219
|
*/
|
|
159
|
-
|
|
160
|
-
return this.
|
|
220
|
+
getIsEmpty() {
|
|
221
|
+
return this._items.length === 0;
|
|
161
222
|
}
|
|
162
223
|
/**
|
|
163
224
|
* Returns true if the queuer is full
|
|
164
225
|
*/
|
|
165
|
-
|
|
166
|
-
return this.
|
|
226
|
+
getIsFull() {
|
|
227
|
+
return this._items.length >= this._options.maxSize;
|
|
167
228
|
}
|
|
168
229
|
/**
|
|
169
230
|
* Returns the current size of the queuer
|
|
170
231
|
*/
|
|
171
|
-
|
|
172
|
-
return this.
|
|
232
|
+
getSize() {
|
|
233
|
+
return this._items.length;
|
|
173
234
|
}
|
|
174
235
|
/**
|
|
175
|
-
*
|
|
236
|
+
* Returns a copy of all items in the queuer
|
|
176
237
|
*/
|
|
177
|
-
|
|
178
|
-
this.
|
|
179
|
-
this.options.onUpdate(this);
|
|
238
|
+
getAllItems() {
|
|
239
|
+
return [...this.getActiveItems(), ...this.getPendingItems()];
|
|
180
240
|
}
|
|
181
241
|
/**
|
|
182
|
-
*
|
|
242
|
+
* Returns the active items
|
|
183
243
|
*/
|
|
184
|
-
|
|
185
|
-
this.
|
|
186
|
-
this.executionCount = 0;
|
|
187
|
-
if (withInitialItems) {
|
|
188
|
-
this.items = [...this.options.initialItems];
|
|
189
|
-
}
|
|
190
|
-
this.running = this.options.started;
|
|
244
|
+
getActiveItems() {
|
|
245
|
+
return Array.from(this._activeItems);
|
|
191
246
|
}
|
|
192
247
|
/**
|
|
193
|
-
* Returns
|
|
248
|
+
* Returns the pending items
|
|
194
249
|
*/
|
|
195
|
-
|
|
196
|
-
return [...this.
|
|
250
|
+
getPendingItems() {
|
|
251
|
+
return [...this._items];
|
|
197
252
|
}
|
|
198
253
|
/**
|
|
199
254
|
* Returns the number of items that have been removed from the queuer
|
|
200
255
|
*/
|
|
201
256
|
getExecutionCount() {
|
|
202
|
-
return this.
|
|
257
|
+
return this._executionCount;
|
|
203
258
|
}
|
|
204
259
|
/**
|
|
205
|
-
* Returns the
|
|
260
|
+
* Returns the number of items that have been rejected from the queuer
|
|
206
261
|
*/
|
|
207
|
-
|
|
208
|
-
return
|
|
262
|
+
getRejectionCount() {
|
|
263
|
+
return this._rejectionCount;
|
|
209
264
|
}
|
|
210
265
|
/**
|
|
211
|
-
* Returns the
|
|
266
|
+
* Returns true if the queuer is running
|
|
212
267
|
*/
|
|
213
|
-
|
|
214
|
-
return this.
|
|
268
|
+
getIsRunning() {
|
|
269
|
+
return this._running;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Returns true if the queuer is running but has no items to process
|
|
273
|
+
*/
|
|
274
|
+
getIsIdle() {
|
|
275
|
+
return this._running && this.getIsEmpty() && this._activeItems.size === 0;
|
|
215
276
|
}
|
|
216
277
|
/**
|
|
217
278
|
* Adds a callback to be called when a task succeeds
|
|
218
279
|
*/
|
|
219
280
|
onSuccess(cb) {
|
|
220
|
-
this.
|
|
281
|
+
this._onSuccessCallbacks.push(cb);
|
|
221
282
|
return () => {
|
|
222
|
-
this.
|
|
283
|
+
this._onSuccessCallbacks = this._onSuccessCallbacks.filter(
|
|
284
|
+
(d) => d !== cb
|
|
285
|
+
);
|
|
223
286
|
};
|
|
224
287
|
}
|
|
225
288
|
/**
|
|
226
289
|
* Adds a callback to be called when a task errors
|
|
227
290
|
*/
|
|
228
291
|
onError(cb) {
|
|
229
|
-
this.
|
|
292
|
+
this._onErrorCallbacks.push(cb);
|
|
230
293
|
return () => {
|
|
231
|
-
this.
|
|
294
|
+
this._onErrorCallbacks = this._onErrorCallbacks.filter((d) => d !== cb);
|
|
232
295
|
};
|
|
233
296
|
}
|
|
234
297
|
/**
|
|
235
298
|
* Adds a callback to be called when a task is settled
|
|
236
299
|
*/
|
|
237
300
|
onSettled(cb) {
|
|
238
|
-
this.
|
|
301
|
+
this._onSettledCallbacks.push(cb);
|
|
239
302
|
return () => {
|
|
240
|
-
this.
|
|
303
|
+
this._onSettledCallbacks = this._onSettledCallbacks.filter(
|
|
304
|
+
(d) => d !== cb
|
|
305
|
+
);
|
|
241
306
|
};
|
|
242
307
|
}
|
|
243
|
-
/**
|
|
244
|
-
* Starts the queuer and processes items
|
|
245
|
-
*/
|
|
246
|
-
start() {
|
|
247
|
-
this.running = true;
|
|
248
|
-
if (!this.pendingTick && !this.isEmpty()) {
|
|
249
|
-
this.pendingTick = true;
|
|
250
|
-
this.tick();
|
|
251
|
-
}
|
|
252
|
-
this.options.onUpdate(this);
|
|
253
|
-
return new Promise((resolve) => {
|
|
254
|
-
const checkIdle = () => {
|
|
255
|
-
if (this.isIdle()) {
|
|
256
|
-
resolve();
|
|
257
|
-
} else {
|
|
258
|
-
setTimeout(checkIdle, 100);
|
|
259
|
-
}
|
|
260
|
-
};
|
|
261
|
-
checkIdle();
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
/**
|
|
265
|
-
* Stops the queuer from processing items
|
|
266
|
-
*/
|
|
267
|
-
stop() {
|
|
268
|
-
this.running = false;
|
|
269
|
-
this.pendingTick = false;
|
|
270
|
-
this.options.onUpdate(this);
|
|
271
|
-
}
|
|
272
|
-
/**
|
|
273
|
-
* Returns true if the queuer is running
|
|
274
|
-
*/
|
|
275
|
-
isRunning() {
|
|
276
|
-
return this.running;
|
|
277
|
-
}
|
|
278
|
-
/**
|
|
279
|
-
* Returns true if the queuer is running but has no items to process
|
|
280
|
-
*/
|
|
281
|
-
isIdle() {
|
|
282
|
-
return this.running && this.isEmpty() && this.activeItems.size === 0;
|
|
283
|
-
}
|
|
284
308
|
}
|
|
285
309
|
function asyncQueue(options = {}) {
|
|
286
310
|
const queuer = new AsyncQueuer({ ...options, started: true });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"async-queuer.js","sources":["../../src/async-queuer.ts"],"sourcesContent":["import type { QueuePosition } from './queuer'\n\nexport interface AsyncQueuerOptions<TValue> {\n /**\n * Default position to add items to the queuer\n * @default 'back'\n */\n addItemsTo?: QueuePosition\n /**\n * Maximum number of concurrent tasks to process\n */\n concurrency?: number\n /**\n * Default position to get items from during processing\n * @default 'front'\n */\n getItemsFrom?: QueuePosition\n /**\n * Function to determine priority of items in the queuer\n * Higher priority items will be processed first\n * If not provided, will use static priority values attached to tasks\n */\n getPriority?: (item: () => Promise<TValue>) => number\n /**\n * Initial items to populate the queuer with\n */\n initialItems?: Array<(() => Promise<TValue>) & { priority?: number }>\n /**\n * Maximum number of items allowed in the queuer\n */\n maxSize?: number\n /**\n * Callback fired whenever an item is removed from the queuer\n */\n onGetNextItem?: (\n item: () => Promise<TValue>,\n queuer: AsyncQueuer<TValue>,\n ) => void\n /**\n * Callback fired whenever an item is added or removed from the queuer\n */\n onUpdate?: (queuer: AsyncQueuer<TValue>) => void\n /**\n * Whether the queuer should start processing tasks immediately\n */\n started?: boolean\n /**\n * Time in milliseconds to wait between processing items\n */\n wait?: number\n}\n\nconst defaultOptions: Required<AsyncQueuerOptions<any>> = {\n addItemsTo: 'back',\n concurrency: 1,\n getItemsFrom: 'front',\n getPriority: (item) => (item as any).priority ?? 0,\n initialItems: [],\n maxSize: Infinity,\n onGetNextItem: () => {},\n onUpdate: () => {},\n started: false,\n wait: 0,\n}\n\n/**\n * A flexible asynchronous queuer that processes tasks with configurable concurrency control.\n *\n * Features:\n * - Priority queuer support via getPriority option\n * - Configurable concurrency limit\n * - Task success/error/completion callbacks\n * - FIFO (First In First Out) or LIFO (Last In First Out) queuer behavior\n * - Pause/resume task processing\n * - Task cancellation\n *\n * Tasks are processed concurrently up to the configured concurrency limit. When a task completes,\n * the next pending task is processed if below the concurrency limit.\n *\n * @example\n * ```ts\n * const queuer = new AsyncQueuer<string>({ concurrency: 2 });\n *\n * queuer.addItem(async () => {\n * return 'Hello';\n * });\n *\n * queuer.start();\n *\n * queuer.onSuccess((result) => {\n * console.log(result); // 'Hello'\n * });\n * ```\n */\nexport class AsyncQueuer<TValue> {\n protected options: Required<AsyncQueuerOptions<TValue>>\n private items: Array<() => Promise<TValue>> = []\n private activeItems: Set<() => Promise<TValue>> = new Set()\n private onSuccessCallbacks: Array<(result: TValue) => void> = []\n private onErrorCallbacks: Array<(error: Error) => void> = []\n private onSettledCallbacks: Array<(result: TValue | Error) => void> = []\n private running: boolean\n private pendingTick = false\n private executionCount = 0\n\n constructor(initialOptions: AsyncQueuerOptions<TValue> = defaultOptions) {\n this.options = { ...defaultOptions, ...initialOptions }\n this.running = this.options.started\n\n for (let i = 0; i < this.options.initialItems.length; i++) {\n const item = this.options.initialItems[i]!\n const isLast = i === this.options.initialItems.length - 1\n this.addItem(item, this.options.addItemsTo, isLast)\n }\n }\n\n /**\n * Updates the queuer options\n * Returns the new options state\n */\n setOptions(\n newOptions: Partial<AsyncQueuerOptions<TValue>>,\n ): AsyncQueuerOptions<TValue> {\n this.options = { ...this.options, ...newOptions }\n return this.options\n }\n\n /**\n * Processes items in the queuer\n */\n protected tick() {\n if (!this.running) {\n this.pendingTick = false\n return\n }\n\n while (\n this.activeItems.size < this.options.concurrency &&\n !this.isEmpty()\n ) {\n const nextFn = this.getNextItem()\n if (!nextFn) {\n break\n }\n\n this.activeItems.add(nextFn)\n ;(async () => {\n let success = false\n let res!: TValue\n let error: Error | undefined\n\n try {\n res = await nextFn()\n success = true\n } catch (e) {\n error = e as Error\n } finally {\n this.options.onUpdate(this)\n }\n\n this.activeItems.delete(nextFn)\n if (success) {\n this.onSuccessCallbacks.forEach((cb) => cb(res))\n } else {\n this.onErrorCallbacks.forEach((cb) => cb(error!))\n }\n this.onSettledCallbacks.forEach((cb) => cb(success ? res : error!))\n\n if (this.options.wait > 0) {\n setTimeout(() => this.tick(), this.options.wait)\n return\n }\n\n this.tick()\n })()\n }\n\n this.pendingTick = false\n }\n\n /**\n * Adds a task to the queuer\n */\n addItem(\n fn: (() => Promise<TValue>) & { priority?: number },\n position: QueuePosition = this.options.addItemsTo,\n runOnUpdate: boolean = true,\n ): Promise<TValue> {\n if (this.isFull()) {\n return Promise.reject(new Error('Queuer is full'))\n }\n\n return new Promise<TValue>((resolve, reject) => {\n const task = Object.assign(\n async () => {\n try {\n const result = await fn()\n resolve(result)\n return result\n } catch (error) {\n reject(error)\n throw error\n }\n },\n { priority: fn.priority ?? undefined },\n )\n\n // Get priority either from the function or from getPriority option\n const priority =\n this.options.getPriority !== defaultOptions.getPriority\n ? this.options.getPriority(task)\n : task.priority\n\n if (priority !== undefined) {\n // Insert based on priority\n const insertIndex = this.items.findIndex((existing) => {\n const existingPriority =\n this.options.getPriority !== defaultOptions.getPriority\n ? this.options.getPriority(existing)\n : (existing as any).priority\n return existingPriority > priority\n })\n\n if (insertIndex === -1) {\n this.items.push(task)\n } else {\n this.items.splice(insertIndex, 0, task)\n }\n } else {\n // Default FIFO/LIFO behavior\n if (position === 'front') {\n this.items.unshift(task)\n } else {\n this.items.push(task)\n }\n }\n\n if (runOnUpdate) {\n this.options.onUpdate(this)\n }\n\n if (this.running && !this.pendingTick) {\n this.pendingTick = true\n this.tick()\n }\n })\n }\n\n /**\n * Removes and returns an item from the queuer\n */\n getNextItem(\n position: QueuePosition = this.options.getItemsFrom,\n ): (() => Promise<TValue>) | undefined {\n let item: (() => Promise<TValue>) | undefined\n\n if (position === 'front') {\n item = this.items.shift()\n } else {\n item = this.items.pop()\n }\n\n if (item !== undefined) {\n this.executionCount++\n this.options.onUpdate(this)\n this.options.onGetNextItem(item, this)\n }\n return item\n }\n\n /**\n * Returns an item without removing it\n */\n peek(position: QueuePosition = 'front'): (() => Promise<TValue>) | undefined {\n if (position === 'front') {\n return this.items[0]\n }\n return this.items[this.items.length - 1]\n }\n\n /**\n * Returns true if the queuer is empty\n */\n isEmpty(): boolean {\n return this.items.length === 0\n }\n\n /**\n * Returns true if the queuer is full\n */\n isFull(): boolean {\n return this.items.length >= this.options.maxSize\n }\n\n /**\n * Returns the current size of the queuer\n */\n size(): number {\n return this.items.length\n }\n\n /**\n * Removes all items from the queuer\n */\n clear(): void {\n this.items = []\n this.options.onUpdate(this)\n }\n\n /**\n * Resets the queuer to its initial state\n */\n reset(withInitialItems?: boolean): void {\n this.clear()\n this.executionCount = 0\n if (withInitialItems) {\n this.items = [...this.options.initialItems]\n }\n this.running = this.options.started\n }\n\n /**\n * Returns a copy of all items in the queuer\n */\n getAllItems(): Array<() => Promise<TValue>> {\n return [...this.items]\n }\n\n /**\n * Returns the number of items that have been removed from the queuer\n */\n getExecutionCount(): number {\n return this.executionCount\n }\n\n /**\n * Returns the active items\n */\n getActiveItems(): Array<() => Promise<TValue>> {\n return Array.from(this.activeItems)\n }\n\n /**\n * Returns the pending items\n */\n getPendingItems(): Array<() => Promise<TValue>> {\n return this.getAllItems()\n }\n\n /**\n * Adds a callback to be called when a task succeeds\n */\n onSuccess(cb: (result: TValue) => void) {\n this.onSuccessCallbacks.push(cb)\n return () => {\n this.onSuccessCallbacks = this.onSuccessCallbacks.filter((d) => d !== cb)\n }\n }\n\n /**\n * Adds a callback to be called when a task errors\n */\n onError(cb: (error: Error) => void) {\n this.onErrorCallbacks.push(cb)\n return () => {\n this.onErrorCallbacks = this.onErrorCallbacks.filter((d) => d !== cb)\n }\n }\n\n /**\n * Adds a callback to be called when a task is settled\n */\n onSettled(cb: (result: TValue | Error) => void) {\n this.onSettledCallbacks.push(cb)\n return () => {\n this.onSettledCallbacks = this.onSettledCallbacks.filter((d) => d !== cb)\n }\n }\n\n /**\n * Starts the queuer and processes items\n */\n start(): Promise<void> {\n this.running = true\n if (!this.pendingTick && !this.isEmpty()) {\n this.pendingTick = true\n this.tick()\n }\n this.options.onUpdate(this)\n\n return new Promise<void>((resolve) => {\n const checkIdle = () => {\n if (this.isIdle()) {\n resolve()\n } else {\n setTimeout(checkIdle, 100)\n }\n }\n checkIdle()\n })\n }\n\n /**\n * Stops the queuer from processing items\n */\n stop(): void {\n this.running = false\n this.pendingTick = false\n this.options.onUpdate(this)\n }\n\n /**\n * Returns true if the queuer is running\n */\n isRunning(): boolean {\n return this.running\n }\n\n /**\n * Returns true if the queuer is running but has no items to process\n */\n isIdle(): boolean {\n return this.running && this.isEmpty() && this.activeItems.size === 0\n }\n}\n\n/**\n * Creates a new AsyncQueuer instance with the given options and returns a bound addItem function.\n * The queuer is automatically started and ready to process items.\n *\n * @example\n * ```ts\n * const enqueue = asyncQueue<string>();\n *\n * // Add items to be processed\n * enqueue(async () => {\n * return 'Hello';\n * });\n * ```\n *\n * @param options - Configuration options for the AsyncQueuer\n * @returns A bound addItem function that can be used to add tasks to the queuer\n */\nexport function asyncQueue<TValue>(options: AsyncQueuerOptions<TValue> = {}) {\n const queuer = new AsyncQueuer<TValue>({ ...options, started: true })\n return queuer.addItem.bind(queuer)\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"async-queuer.js","sources":["../../src/async-queuer.ts"],"sourcesContent":["import type { QueuePosition } from './queuer'\n\nexport interface AsyncQueuerOptions<TValue> {\n /**\n * Default position to add items to the queuer\n * @default 'back'\n */\n addItemsTo?: QueuePosition\n /**\n * Maximum number of concurrent tasks to process\n */\n concurrency?: number\n /**\n * Default position to get items from during processing\n * @default 'front'\n */\n getItemsFrom?: QueuePosition\n /**\n * Function to determine priority of items in the queuer\n * Higher priority items will be processed first\n * If not provided, will use static priority values attached to tasks\n */\n getPriority?: (item: () => Promise<TValue>) => number\n /**\n * Initial items to populate the queuer with\n */\n initialItems?: Array<(() => Promise<TValue>) & { priority?: number }>\n /**\n * Maximum number of items allowed in the queuer\n */\n maxSize?: number\n /**\n * Callback fired whenever an item is removed from the queuer\n */\n onGetNextItem?: (\n item: () => Promise<TValue>,\n queuer: AsyncQueuer<TValue>,\n ) => void\n /**\n * Callback fired whenever the queuer's running state changes\n */\n onIsRunningChange?: (queuer: AsyncQueuer<TValue>) => void\n /**\n * Callback fired whenever an item is added or removed from the queuer\n */\n onItemsChange?: (queuer: AsyncQueuer<TValue>) => void\n /**\n * Callback fired whenever an item is rejected from being added to the queuer\n */\n onReject?: (item: () => Promise<TValue>, queuer: AsyncQueuer<TValue>) => void\n /**\n * Whether the queuer should start processing tasks immediately\n */\n started?: boolean\n /**\n * Time in milliseconds to wait between processing items\n */\n wait?: number\n}\n\nconst defaultOptions: Required<AsyncQueuerOptions<any>> = {\n addItemsTo: 'back',\n concurrency: 1,\n getItemsFrom: 'front',\n getPriority: (item) => (item as any)?.priority ?? 0,\n initialItems: [],\n maxSize: Infinity,\n onGetNextItem: () => {},\n onIsRunningChange: () => {},\n onItemsChange: () => {},\n onReject: () => {},\n started: false,\n wait: 0,\n}\n\n/**\n * A flexible asynchronous queue that processes tasks with configurable concurrency control.\n *\n * Features:\n * - Priority queue support via getPriority option\n * - Configurable concurrency limit\n * - Task success/error/completion callbacks\n * - FIFO (First In First Out) or LIFO (Last In First Out) queue behavior\n * - Pause/resume task processing\n * - Task cancellation\n *\n * Tasks are processed concurrently up to the configured concurrency limit. When a task completes,\n * the next pending task is processed if below the concurrency limit.\n *\n * @example\n * ```ts\n * const asyncQueuer = new AsyncQueuer<string>({ concurrency: 2 });\n *\n * asyncQueuer.addItem(async () => {\n * return 'Hello';\n * });\n *\n * asyncQueuer.start();\n *\n * asyncQueuer.onSuccess((result) => {\n * console.log(result); // 'Hello'\n * });\n * ```\n */\nexport class AsyncQueuer<TValue> {\n private _options: Required<AsyncQueuerOptions<TValue>>\n private _activeItems: Set<() => Promise<TValue>> = new Set()\n private _executionCount = 0\n private _rejectionCount = 0\n private _items: Array<() => Promise<TValue>> = []\n private _onErrorCallbacks: Array<(error: Error) => void> = []\n private _onSettledCallbacks: Array<(result: TValue | Error) => void> = []\n private _onSuccessCallbacks: Array<(result: TValue) => void> = []\n private _pendingTick = false\n private _running: boolean\n\n constructor(initialOptions: AsyncQueuerOptions<TValue> = defaultOptions) {\n this._options = { ...defaultOptions, ...initialOptions }\n this._running = this._options.started\n\n for (let i = 0; i < this._options.initialItems.length; i++) {\n const item = this._options.initialItems[i]!\n const isLast = i === this._options.initialItems.length - 1\n this.addItem(item, this._options.addItemsTo, isLast)\n }\n }\n\n /**\n * Updates the queuer options\n * Returns the new options state\n */\n setOptions(\n newOptions: Partial<AsyncQueuerOptions<TValue>>,\n ): AsyncQueuerOptions<TValue> {\n this._options = { ...this._options, ...newOptions }\n return this._options\n }\n\n /**\n * Returns the current queuer options\n */\n getOptions(): Required<AsyncQueuerOptions<TValue>> {\n return this._options\n }\n\n /**\n * Processes items in the queuer\n */\n private tick() {\n if (!this._running) {\n this._pendingTick = false\n return\n }\n\n while (\n this._activeItems.size < this._options.concurrency &&\n !this.getIsEmpty()\n ) {\n const nextFn = this.getNextItem()\n if (!nextFn) {\n break\n }\n this._activeItems.add(nextFn)\n this._options.onItemsChange(this)\n ;(async () => {\n let success = false\n let res!: TValue\n let error: Error | undefined\n\n try {\n res = await nextFn()\n success = true\n } catch (e) {\n error = e as Error\n } finally {\n this._activeItems.delete(nextFn)\n this._options.onItemsChange(this)\n }\n\n if (success) {\n this._onSuccessCallbacks.forEach((cb) => cb(res))\n } else {\n this._onErrorCallbacks.forEach((cb) => cb(error!))\n }\n this._onSettledCallbacks.forEach((cb) => cb(success ? res : error!))\n\n if (this._options.wait > 0) {\n setTimeout(() => this.tick(), this._options.wait)\n return\n }\n\n this.tick()\n })()\n }\n\n this._pendingTick = false\n }\n\n /**\n * Starts the queuer and processes items\n */\n start(): Promise<void> {\n this._running = true\n if (!this._pendingTick && !this.getIsEmpty()) {\n this._pendingTick = true\n this.tick()\n }\n this._options.onIsRunningChange(this)\n\n return new Promise<void>((resolve) => {\n const checkIdle = () => {\n if (this.getIsIdle()) {\n resolve()\n } else {\n setTimeout(checkIdle, 100)\n }\n }\n checkIdle()\n })\n }\n\n /**\n * Stops the queuer from processing items\n */\n stop(): void {\n this._running = false\n this._pendingTick = false\n this._options.onIsRunningChange(this)\n }\n\n /**\n * Removes all items from the queuer\n */\n clear(): void {\n this._items = []\n this._options.onItemsChange(this)\n }\n\n /**\n * Resets the queuer to its initial state\n */\n reset(withInitialItems?: boolean): void {\n this.clear()\n this._executionCount = 0\n if (withInitialItems) {\n this._items = [...this._options.initialItems]\n }\n this._running = this._options.started\n }\n\n /**\n * Adds a task to the queuer\n */\n addItem(\n fn: (() => Promise<TValue>) & { priority?: number },\n position: QueuePosition = this._options.addItemsTo,\n runOnUpdate: boolean = true,\n ): Promise<TValue> {\n if (this.getIsFull()) {\n this._rejectionCount++\n this._options.onReject(fn, this)\n return Promise.reject(new Error('Queuer is full'))\n }\n\n return new Promise<TValue>((resolve, reject) => {\n const task = Object.assign(\n async () => {\n try {\n const result = await fn()\n resolve(result)\n return result\n } catch (error) {\n reject(error)\n throw error\n }\n },\n { priority: fn.priority ?? undefined },\n )\n\n // Get priority either from the function or from getPriority option\n const priority =\n this._options.getPriority !== defaultOptions.getPriority\n ? this._options.getPriority(task)\n : task.priority\n\n if (priority !== undefined) {\n // Insert based on priority\n const insertIndex = this._items.findIndex((existing) => {\n const existingPriority =\n this._options.getPriority !== defaultOptions.getPriority\n ? this._options.getPriority(existing)\n : (existing as any).priority\n return existingPriority > priority\n })\n\n if (insertIndex === -1) {\n this._items.push(task)\n } else {\n this._items.splice(insertIndex, 0, task)\n }\n } else {\n // Default FIFO/LIFO behavior\n if (position === 'front') {\n this._items.unshift(task)\n } else {\n this._items.push(task)\n }\n }\n\n if (runOnUpdate) {\n this._options.onItemsChange(this)\n }\n\n if (this._running && !this._pendingTick) {\n this._pendingTick = true\n this.tick()\n }\n })\n }\n\n /**\n * Removes and returns an item from the queuer\n */\n getNextItem(\n position: QueuePosition = this._options.getItemsFrom,\n ): (() => Promise<TValue>) | undefined {\n let item: (() => Promise<TValue>) | undefined\n\n if (position === 'front') {\n item = this._items.shift()\n } else {\n item = this._items.pop()\n }\n\n if (item !== undefined) {\n this._executionCount++\n this._options.onItemsChange(this)\n this._options.onGetNextItem(item, this)\n }\n return item\n }\n\n /**\n * Returns an item without removing it\n */\n getPeek(\n position: QueuePosition = 'front',\n ): (() => Promise<TValue>) | undefined {\n if (position === 'front') {\n return this._items[0]\n }\n return this._items[this._items.length - 1]\n }\n\n /**\n * Returns true if the queuer is empty\n */\n getIsEmpty(): boolean {\n return this._items.length === 0\n }\n\n /**\n * Returns true if the queuer is full\n */\n getIsFull(): boolean {\n return this._items.length >= this._options.maxSize\n }\n\n /**\n * Returns the current size of the queuer\n */\n getSize(): number {\n return this._items.length\n }\n\n /**\n * Returns a copy of all items in the queuer\n */\n getAllItems(): Array<() => Promise<TValue>> {\n return [...this.getActiveItems(), ...this.getPendingItems()]\n }\n\n /**\n * Returns the active items\n */\n getActiveItems(): Array<() => Promise<TValue>> {\n return Array.from(this._activeItems)\n }\n\n /**\n * Returns the pending items\n */\n getPendingItems(): Array<() => Promise<TValue>> {\n return [...this._items]\n }\n\n /**\n * Returns the number of items that have been removed from the queuer\n */\n getExecutionCount(): number {\n return this._executionCount\n }\n\n /**\n * Returns the number of items that have been rejected from the queuer\n */\n getRejectionCount(): number {\n return this._rejectionCount\n }\n\n /**\n * Returns true if the queuer is running\n */\n getIsRunning(): boolean {\n return this._running\n }\n\n /**\n * Returns true if the queuer is running but has no items to process\n */\n getIsIdle(): boolean {\n return this._running && this.getIsEmpty() && this._activeItems.size === 0\n }\n\n /**\n * Adds a callback to be called when a task succeeds\n */\n onSuccess(cb: (result: TValue) => void) {\n this._onSuccessCallbacks.push(cb)\n return () => {\n this._onSuccessCallbacks = this._onSuccessCallbacks.filter(\n (d) => d !== cb,\n )\n }\n }\n\n /**\n * Adds a callback to be called when a task errors\n */\n onError(cb: (error: Error) => void) {\n this._onErrorCallbacks.push(cb)\n return () => {\n this._onErrorCallbacks = this._onErrorCallbacks.filter((d) => d !== cb)\n }\n }\n\n /**\n * Adds a callback to be called when a task is settled\n */\n onSettled(cb: (result: TValue | Error) => void) {\n this._onSettledCallbacks.push(cb)\n return () => {\n this._onSettledCallbacks = this._onSettledCallbacks.filter(\n (d) => d !== cb,\n )\n }\n }\n}\n\n/**\n * Creates a new AsyncQueuer instance with the given options and returns a bound addItem function.\n * The queuer is automatically started and ready to process items.\n *\n * @example\n * ```ts\n * const enqueue = asyncQueue<string>();\n *\n * // Add items to be processed\n * enqueue(async () => {\n * return 'Hello';\n * });\n * ```\n *\n * @param options - Configuration options for the AsyncQueuer\n * @returns A bound addItem function that can be used to add tasks to the queuer\n */\nexport function asyncQueue<TValue>(options: AsyncQueuerOptions<TValue> = {}) {\n const queuer = new AsyncQueuer<TValue>({ ...options, started: true })\n return queuer.addItem.bind(queuer)\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AnyAsyncFunction } from './types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Options for configuring an async rate-limited function
|
|
4
4
|
*/
|
|
5
|
-
export interface AsyncRateLimiterOptions {
|
|
5
|
+
export interface AsyncRateLimiterOptions<TFn extends AnyAsyncFunction, TArgs extends Parameters<TFn>> {
|
|
6
6
|
/**
|
|
7
7
|
* Whether the rate limiter is enabled. When disabled, maybeExecute will not trigger any executions.
|
|
8
8
|
* Defaults to true.
|
|
@@ -13,17 +13,21 @@ export interface AsyncRateLimiterOptions {
|
|
|
13
13
|
*/
|
|
14
14
|
limit: number;
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
16
|
+
* Optional error handler for when the rate-limited function throws
|
|
17
17
|
*/
|
|
18
|
-
|
|
18
|
+
onError?: (error: unknown) => void;
|
|
19
|
+
/**
|
|
20
|
+
* Optional function to call when the rate-limited function is executed
|
|
21
|
+
*/
|
|
22
|
+
onExecute?: (rateLimiter: AsyncRateLimiter<TFn, TArgs>) => void;
|
|
19
23
|
/**
|
|
20
24
|
* Optional callback function that is called when an execution is rejected due to rate limiting
|
|
21
25
|
*/
|
|
22
|
-
onReject?: (
|
|
26
|
+
onReject?: (rateLimiter: AsyncRateLimiter<TFn, TArgs>) => void;
|
|
23
27
|
/**
|
|
24
|
-
*
|
|
28
|
+
* Time window in milliseconds within which the limit applies
|
|
25
29
|
*/
|
|
26
|
-
|
|
30
|
+
window: number;
|
|
27
31
|
}
|
|
28
32
|
/**
|
|
29
33
|
* A class that creates an async rate-limited function.
|
|
@@ -50,30 +54,22 @@ export interface AsyncRateLimiterOptions {
|
|
|
50
54
|
* await rateLimiter.maybeExecute('123');
|
|
51
55
|
* ```
|
|
52
56
|
*/
|
|
53
|
-
export declare class AsyncRateLimiter<TFn extends
|
|
57
|
+
export declare class AsyncRateLimiter<TFn extends AnyAsyncFunction, TArgs extends Parameters<TFn>> {
|
|
54
58
|
private fn;
|
|
55
|
-
private
|
|
56
|
-
private
|
|
57
|
-
private
|
|
58
|
-
private
|
|
59
|
-
constructor(fn: TFn, initialOptions: AsyncRateLimiterOptions);
|
|
59
|
+
private _executionCount;
|
|
60
|
+
private _executionTimes;
|
|
61
|
+
private _options;
|
|
62
|
+
private _rejectionCount;
|
|
63
|
+
constructor(fn: TFn, initialOptions: AsyncRateLimiterOptions<TFn, TArgs>);
|
|
60
64
|
/**
|
|
61
65
|
* Updates the rate limiter options
|
|
62
66
|
* Returns the new options state
|
|
63
67
|
*/
|
|
64
|
-
setOptions(newOptions: Partial<AsyncRateLimiterOptions
|
|
65
|
-
/**
|
|
66
|
-
* Returns the number of times the function has been executed
|
|
67
|
-
*/
|
|
68
|
-
getExecutionCount(): number;
|
|
69
|
-
/**
|
|
70
|
-
* Returns the number of times the function has been rejected
|
|
71
|
-
*/
|
|
72
|
-
getRejectionCount(): number;
|
|
68
|
+
setOptions(newOptions: Partial<AsyncRateLimiterOptions<TFn, TArgs>>): AsyncRateLimiterOptions<TFn, TArgs>;
|
|
73
69
|
/**
|
|
74
|
-
* Returns the
|
|
70
|
+
* Returns the current rate limiter options
|
|
75
71
|
*/
|
|
76
|
-
|
|
72
|
+
getOptions(): Required<AsyncRateLimiterOptions<TFn, TArgs>>;
|
|
77
73
|
/**
|
|
78
74
|
* Attempts to execute the rate-limited function if within the configured limits.
|
|
79
75
|
* Will reject execution if the number of calls in the current window exceeds the limit.
|
|
@@ -94,6 +90,22 @@ export declare class AsyncRateLimiter<TFn extends (...args: Array<any>) => Promi
|
|
|
94
90
|
private executeFunction;
|
|
95
91
|
private rejectFunction;
|
|
96
92
|
private cleanupOldExecutions;
|
|
93
|
+
/**
|
|
94
|
+
* Returns the number of times the function has been executed
|
|
95
|
+
*/
|
|
96
|
+
getExecutionCount(): number;
|
|
97
|
+
/**
|
|
98
|
+
* Returns the number of times the function has been rejected
|
|
99
|
+
*/
|
|
100
|
+
getRejectionCount(): number;
|
|
101
|
+
/**
|
|
102
|
+
* Returns the number of remaining executions allowed in the current window
|
|
103
|
+
*/
|
|
104
|
+
getRemainingInWindow(): number;
|
|
105
|
+
/**
|
|
106
|
+
* Returns the number of milliseconds until the next execution will be possible
|
|
107
|
+
*/
|
|
108
|
+
getMsUntilNextWindow(): number;
|
|
97
109
|
/**
|
|
98
110
|
* Resets the rate limiter state
|
|
99
111
|
*/
|
|
@@ -116,8 +128,8 @@ export declare class AsyncRateLimiter<TFn extends (...args: Array<any>) => Promi
|
|
|
116
128
|
* const rateLimited = asyncRateLimit(makeApiCall, {
|
|
117
129
|
* limit: 5,
|
|
118
130
|
* window: 60000,
|
|
119
|
-
* onReject: (
|
|
120
|
-
* console.log(`Rate limit exceeded. Try again in ${
|
|
131
|
+
* onReject: (rateLimiter) => {
|
|
132
|
+
* console.log(`Rate limit exceeded. Try again in ${rateLimiter.getMsUntilNextWindow()}ms`);
|
|
121
133
|
* }
|
|
122
134
|
* });
|
|
123
135
|
*
|
|
@@ -129,4 +141,4 @@ export declare class AsyncRateLimiter<TFn extends (...args: Array<any>) => Promi
|
|
|
129
141
|
* const throttled = throttle(makeApiCall, { wait: 12000 }); // One call every 12 seconds
|
|
130
142
|
* ```
|
|
131
143
|
*/
|
|
132
|
-
export declare function asyncRateLimit<TFn extends
|
|
144
|
+
export declare function asyncRateLimit<TFn extends AnyAsyncFunction, TArgs extends Parameters<TFn>>(fn: TFn, initialOptions: Omit<AsyncRateLimiterOptions<TFn, TArgs>, 'enabled'>): (...args: TArgs) => Promise<boolean>;
|