@tanstack/pacer 0.5.0 → 0.7.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 +32 -17
- package/dist/cjs/async-debouncer.cjs.map +1 -1
- package/dist/cjs/async-debouncer.d.cts +51 -9
- package/dist/cjs/async-queuer.cjs +189 -191
- package/dist/cjs/async-queuer.cjs.map +1 -1
- package/dist/cjs/async-queuer.d.cts +151 -94
- package/dist/cjs/async-rate-limiter.cjs +23 -13
- package/dist/cjs/async-rate-limiter.cjs.map +1 -1
- package/dist/cjs/async-rate-limiter.d.cts +54 -5
- package/dist/cjs/async-throttler.cjs +38 -17
- package/dist/cjs/async-throttler.cjs.map +1 -1
- package/dist/cjs/async-throttler.d.cts +51 -8
- package/dist/cjs/batcher.cjs +138 -0
- package/dist/cjs/batcher.cjs.map +1 -0
- package/dist/cjs/batcher.d.cts +149 -0
- package/dist/cjs/debouncer.cjs +3 -4
- package/dist/cjs/debouncer.cjs.map +1 -1
- package/dist/cjs/debouncer.d.cts +1 -2
- package/dist/cjs/index.cjs +3 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +1 -0
- package/dist/cjs/queuer.cjs +68 -41
- package/dist/cjs/queuer.cjs.map +1 -1
- package/dist/cjs/queuer.d.cts +123 -92
- package/dist/cjs/rate-limiter.cjs +3 -4
- package/dist/cjs/rate-limiter.cjs.map +1 -1
- package/dist/cjs/rate-limiter.d.cts +1 -2
- package/dist/cjs/throttler.cjs +3 -4
- package/dist/cjs/throttler.cjs.map +1 -1
- package/dist/cjs/throttler.d.cts +1 -2
- package/dist/cjs/types.d.cts +1 -0
- package/dist/esm/async-debouncer.d.ts +51 -9
- package/dist/esm/async-debouncer.js +32 -17
- package/dist/esm/async-debouncer.js.map +1 -1
- package/dist/esm/async-queuer.d.ts +151 -94
- package/dist/esm/async-queuer.js +189 -191
- package/dist/esm/async-queuer.js.map +1 -1
- package/dist/esm/async-rate-limiter.d.ts +54 -5
- package/dist/esm/async-rate-limiter.js +23 -13
- package/dist/esm/async-rate-limiter.js.map +1 -1
- package/dist/esm/async-throttler.d.ts +51 -8
- package/dist/esm/async-throttler.js +38 -17
- package/dist/esm/async-throttler.js.map +1 -1
- package/dist/esm/batcher.d.ts +149 -0
- package/dist/esm/batcher.js +138 -0
- package/dist/esm/batcher.js.map +1 -0
- package/dist/esm/debouncer.d.ts +1 -2
- package/dist/esm/debouncer.js +3 -4
- package/dist/esm/debouncer.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +3 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/queuer.d.ts +123 -92
- package/dist/esm/queuer.js +68 -41
- package/dist/esm/queuer.js.map +1 -1
- package/dist/esm/rate-limiter.d.ts +1 -2
- package/dist/esm/rate-limiter.js +3 -4
- package/dist/esm/rate-limiter.js.map +1 -1
- package/dist/esm/throttler.d.ts +1 -2
- package/dist/esm/throttler.js +3 -4
- package/dist/esm/throttler.js.map +1 -1
- package/dist/esm/types.d.ts +1 -0
- package/package.json +11 -1
- package/src/async-debouncer.ts +76 -20
- package/src/async-queuer.ts +309 -275
- package/src/async-rate-limiter.ts +73 -16
- package/src/async-throttler.ts +84 -20
- package/src/batcher.ts +253 -0
- package/src/debouncer.ts +3 -4
- package/src/index.ts +1 -0
- package/src/queuer.ts +142 -98
- package/src/rate-limiter.ts +3 -4
- package/src/throttler.ts +3 -4
- package/src/types.ts +3 -0
package/dist/esm/async-queuer.js
CHANGED
|
@@ -8,32 +8,26 @@ const defaultOptions = {
|
|
|
8
8
|
getPriority: (item) => (item == null ? void 0 : item.priority) ?? 0,
|
|
9
9
|
initialItems: [],
|
|
10
10
|
maxSize: Infinity,
|
|
11
|
-
onGetNextItem: () => {
|
|
12
|
-
},
|
|
13
|
-
onIsRunningChange: () => {
|
|
14
|
-
},
|
|
15
|
-
onItemsChange: () => {
|
|
16
|
-
},
|
|
17
|
-
onReject: () => {
|
|
18
|
-
},
|
|
19
|
-
onExpire: () => {
|
|
20
|
-
},
|
|
21
11
|
started: true,
|
|
22
12
|
wait: 0
|
|
23
13
|
};
|
|
24
14
|
class AsyncQueuer {
|
|
25
|
-
constructor(initialOptions
|
|
15
|
+
constructor(fn, initialOptions) {
|
|
16
|
+
this.fn = fn;
|
|
26
17
|
this._activeItems = /* @__PURE__ */ new Set();
|
|
27
|
-
this.
|
|
18
|
+
this._successCount = 0;
|
|
19
|
+
this._errorCount = 0;
|
|
20
|
+
this._settledCount = 0;
|
|
28
21
|
this._rejectionCount = 0;
|
|
29
22
|
this._expirationCount = 0;
|
|
30
23
|
this._items = [];
|
|
31
24
|
this._itemTimestamps = [];
|
|
32
|
-
this._onErrorCallbacks = [];
|
|
33
|
-
this._onSettledCallbacks = [];
|
|
34
|
-
this._onSuccessCallbacks = [];
|
|
35
25
|
this._pendingTick = false;
|
|
36
|
-
this._options = {
|
|
26
|
+
this._options = {
|
|
27
|
+
...defaultOptions,
|
|
28
|
+
...initialOptions,
|
|
29
|
+
throwOnError: initialOptions.throwOnError ?? !initialOptions.onError
|
|
30
|
+
};
|
|
37
31
|
this._running = this._options.started;
|
|
38
32
|
for (let i = 0; i < this._options.initialItems.length; i++) {
|
|
39
33
|
const item = this._options.initialItems[i];
|
|
@@ -42,65 +36,50 @@ class AsyncQueuer {
|
|
|
42
36
|
}
|
|
43
37
|
}
|
|
44
38
|
/**
|
|
45
|
-
* Updates the queuer options
|
|
46
|
-
* Returns the new options state
|
|
39
|
+
* Updates the queuer options. New options are merged with existing options.
|
|
47
40
|
*/
|
|
48
41
|
setOptions(newOptions) {
|
|
49
42
|
this._options = { ...this._options, ...newOptions };
|
|
50
43
|
}
|
|
51
44
|
/**
|
|
52
|
-
* Returns the current queuer options
|
|
45
|
+
* Returns the current queuer options, including defaults and any overrides.
|
|
53
46
|
*/
|
|
54
47
|
getOptions() {
|
|
55
48
|
return this._options;
|
|
56
49
|
}
|
|
57
50
|
/**
|
|
58
|
-
* Returns the current wait time between processing items
|
|
51
|
+
* Returns the current wait time (in milliseconds) between processing items.
|
|
52
|
+
* If a function is provided, it is called with the queuer instance.
|
|
59
53
|
*/
|
|
60
54
|
getWait() {
|
|
61
55
|
return parseFunctionOrValue(this._options.wait, this);
|
|
62
56
|
}
|
|
63
57
|
/**
|
|
64
|
-
* Returns the current concurrency limit
|
|
58
|
+
* Returns the current concurrency limit for processing items.
|
|
59
|
+
* If a function is provided, it is called with the queuer instance.
|
|
65
60
|
*/
|
|
66
61
|
getConcurrency() {
|
|
67
62
|
return parseFunctionOrValue(this._options.concurrency, this);
|
|
68
63
|
}
|
|
69
64
|
/**
|
|
70
|
-
* Processes items in the
|
|
65
|
+
* Processes items in the queue up to the concurrency limit. Internal use only.
|
|
71
66
|
*/
|
|
72
67
|
tick() {
|
|
68
|
+
var _a, _b;
|
|
73
69
|
if (!this._running) {
|
|
74
70
|
this._pendingTick = false;
|
|
75
71
|
return;
|
|
76
72
|
}
|
|
77
73
|
this.checkExpiredItems();
|
|
78
74
|
while (this._activeItems.size < this.getConcurrency() && !this.getIsEmpty()) {
|
|
79
|
-
const
|
|
80
|
-
if (!
|
|
75
|
+
const nextItem = this.getPeek();
|
|
76
|
+
if (!nextItem) {
|
|
81
77
|
break;
|
|
82
78
|
}
|
|
83
|
-
this._activeItems.add(
|
|
84
|
-
this._options.onItemsChange(this);
|
|
79
|
+
this._activeItems.add(nextItem);
|
|
80
|
+
(_b = (_a = this._options).onItemsChange) == null ? void 0 : _b.call(_a, this);
|
|
85
81
|
(async () => {
|
|
86
|
-
|
|
87
|
-
let res;
|
|
88
|
-
let error;
|
|
89
|
-
try {
|
|
90
|
-
res = await nextFn();
|
|
91
|
-
success = true;
|
|
92
|
-
} catch (e) {
|
|
93
|
-
error = e;
|
|
94
|
-
} finally {
|
|
95
|
-
this._activeItems.delete(nextFn);
|
|
96
|
-
this._options.onItemsChange(this);
|
|
97
|
-
}
|
|
98
|
-
if (success) {
|
|
99
|
-
this._onSuccessCallbacks.forEach((cb) => cb(res));
|
|
100
|
-
} else {
|
|
101
|
-
this._onErrorCallbacks.forEach((cb) => cb(error));
|
|
102
|
-
}
|
|
103
|
-
this._onSettledCallbacks.forEach((cb) => cb(success ? res : error));
|
|
82
|
+
this._lastResult = await this.execute();
|
|
104
83
|
const wait = this.getWait();
|
|
105
84
|
if (wait > 0) {
|
|
106
85
|
setTimeout(() => this.tick(), wait);
|
|
@@ -112,142 +91,109 @@ class AsyncQueuer {
|
|
|
112
91
|
this._pendingTick = false;
|
|
113
92
|
}
|
|
114
93
|
/**
|
|
115
|
-
*
|
|
116
|
-
*/
|
|
117
|
-
checkExpiredItems() {
|
|
118
|
-
if (this._options.expirationDuration === Infinity && this._options.getIsExpired === defaultOptions.getIsExpired)
|
|
119
|
-
return;
|
|
120
|
-
const now = Date.now();
|
|
121
|
-
const expiredIndices = [];
|
|
122
|
-
for (let i = 0; i < this._items.length; i++) {
|
|
123
|
-
const timestamp = this._itemTimestamps[i];
|
|
124
|
-
if (timestamp === void 0) continue;
|
|
125
|
-
const item = this._items[i];
|
|
126
|
-
if (item === void 0) continue;
|
|
127
|
-
const isExpired = this._options.getIsExpired !== defaultOptions.getIsExpired ? this._options.getIsExpired(item, timestamp) : now - timestamp > this._options.expirationDuration;
|
|
128
|
-
if (isExpired) {
|
|
129
|
-
expiredIndices.push(i);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
for (let i = expiredIndices.length - 1; i >= 0; i--) {
|
|
133
|
-
const index = expiredIndices[i];
|
|
134
|
-
if (index === void 0) continue;
|
|
135
|
-
const expiredItem = this._items[index];
|
|
136
|
-
if (expiredItem === void 0) continue;
|
|
137
|
-
this._items.splice(index, 1);
|
|
138
|
-
this._itemTimestamps.splice(index, 1);
|
|
139
|
-
this._expirationCount++;
|
|
140
|
-
this._options.onExpire(expiredItem, this);
|
|
141
|
-
}
|
|
142
|
-
if (expiredIndices.length > 0) {
|
|
143
|
-
this._options.onItemsChange(this);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* Starts the queuer and processes items
|
|
94
|
+
* Starts processing items in the queue. If already running, does nothing.
|
|
148
95
|
*/
|
|
149
96
|
start() {
|
|
97
|
+
var _a, _b;
|
|
150
98
|
this._running = true;
|
|
151
99
|
if (!this._pendingTick && !this.getIsEmpty()) {
|
|
152
100
|
this._pendingTick = true;
|
|
153
101
|
this.tick();
|
|
154
102
|
}
|
|
155
|
-
this._options.onIsRunningChange(this);
|
|
156
|
-
return new Promise((resolve) => {
|
|
157
|
-
const checkIdle = () => {
|
|
158
|
-
if (this.getIsIdle()) {
|
|
159
|
-
resolve();
|
|
160
|
-
} else {
|
|
161
|
-
setTimeout(checkIdle, 100);
|
|
162
|
-
}
|
|
163
|
-
};
|
|
164
|
-
checkIdle();
|
|
165
|
-
});
|
|
103
|
+
(_b = (_a = this._options).onIsRunningChange) == null ? void 0 : _b.call(_a, this);
|
|
166
104
|
}
|
|
167
105
|
/**
|
|
168
|
-
* Stops the
|
|
106
|
+
* Stops processing items in the queue. Does not clear the queue.
|
|
169
107
|
*/
|
|
170
108
|
stop() {
|
|
109
|
+
var _a, _b;
|
|
171
110
|
this._running = false;
|
|
172
111
|
this._pendingTick = false;
|
|
173
|
-
this._options.onIsRunningChange(this);
|
|
112
|
+
(_b = (_a = this._options).onIsRunningChange) == null ? void 0 : _b.call(_a, this);
|
|
174
113
|
}
|
|
175
114
|
/**
|
|
176
|
-
* Removes all items from the
|
|
115
|
+
* Removes all pending items from the queue. Does not affect active tasks.
|
|
177
116
|
*/
|
|
178
117
|
clear() {
|
|
118
|
+
var _a, _b;
|
|
179
119
|
this._items = [];
|
|
180
|
-
this._options.onItemsChange(this);
|
|
120
|
+
(_b = (_a = this._options).onItemsChange) == null ? void 0 : _b.call(_a, this);
|
|
181
121
|
}
|
|
182
122
|
/**
|
|
183
|
-
* Resets the queuer to its initial state
|
|
123
|
+
* Resets the queuer to its initial state. Optionally repopulates with initial items.
|
|
124
|
+
* Does not affect callbacks or options.
|
|
184
125
|
*/
|
|
185
126
|
reset(withInitialItems) {
|
|
186
127
|
this.clear();
|
|
187
|
-
this.
|
|
128
|
+
this._successCount = 0;
|
|
129
|
+
this._errorCount = 0;
|
|
130
|
+
this._settledCount = 0;
|
|
188
131
|
if (withInitialItems) {
|
|
189
132
|
this._items = [...this._options.initialItems];
|
|
190
133
|
}
|
|
191
134
|
this._running = this._options.started;
|
|
192
135
|
}
|
|
193
136
|
/**
|
|
194
|
-
* Adds
|
|
137
|
+
* Adds an item to the queue. If the queue is full, the item is rejected and onReject is called.
|
|
138
|
+
* Items can be inserted based on priority or at the front/back depending on configuration.
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```ts
|
|
142
|
+
* queuer.addItem({ value: 'task', priority: 10 });
|
|
143
|
+
* queuer.addItem('task2', 'front');
|
|
144
|
+
* ```
|
|
195
145
|
*/
|
|
196
|
-
addItem(
|
|
146
|
+
addItem(item, position = this._options.addItemsTo, runOnItemsChange = true) {
|
|
147
|
+
var _a, _b, _c, _d;
|
|
197
148
|
if (this.getIsFull()) {
|
|
198
149
|
this._rejectionCount++;
|
|
199
|
-
this._options.onReject(
|
|
200
|
-
return
|
|
150
|
+
(_b = (_a = this._options).onReject) == null ? void 0 : _b.call(_a, item, this);
|
|
151
|
+
return;
|
|
201
152
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
throw error;
|
|
212
|
-
}
|
|
213
|
-
},
|
|
214
|
-
{ priority: fn.priority ?? void 0 }
|
|
215
|
-
);
|
|
216
|
-
const priority = this._options.getPriority !== defaultOptions.getPriority ? this._options.getPriority(task) : task.priority;
|
|
217
|
-
if (priority !== void 0) {
|
|
218
|
-
const insertIndex = this._items.findIndex((existing) => {
|
|
219
|
-
const existingPriority = this._options.getPriority !== defaultOptions.getPriority ? this._options.getPriority(existing) : existing.priority;
|
|
220
|
-
return existingPriority > priority;
|
|
221
|
-
});
|
|
222
|
-
if (insertIndex === -1) {
|
|
223
|
-
this._items.push(task);
|
|
224
|
-
this._itemTimestamps.push(Date.now());
|
|
225
|
-
} else {
|
|
226
|
-
this._items.splice(insertIndex, 0, task);
|
|
227
|
-
this._itemTimestamps.splice(insertIndex, 0, Date.now());
|
|
228
|
-
}
|
|
153
|
+
const priority = this._options.getPriority !== defaultOptions.getPriority ? this._options.getPriority(item) : item.priority;
|
|
154
|
+
if (priority !== void 0) {
|
|
155
|
+
const insertIndex = this._items.findIndex((existing) => {
|
|
156
|
+
const existingPriority = this._options.getPriority !== defaultOptions.getPriority ? this._options.getPriority(existing) : existing.priority;
|
|
157
|
+
return existingPriority < priority;
|
|
158
|
+
});
|
|
159
|
+
if (insertIndex === -1) {
|
|
160
|
+
this._items.push(item);
|
|
161
|
+
this._itemTimestamps.push(Date.now());
|
|
229
162
|
} else {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
this._itemTimestamps.unshift(Date.now());
|
|
233
|
-
} else {
|
|
234
|
-
this._items.push(task);
|
|
235
|
-
this._itemTimestamps.push(Date.now());
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
if (runOnItemsChange) {
|
|
239
|
-
this._options.onItemsChange(this);
|
|
163
|
+
this._items.splice(insertIndex, 0, item);
|
|
164
|
+
this._itemTimestamps.splice(insertIndex, 0, Date.now());
|
|
240
165
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
this.
|
|
166
|
+
} else {
|
|
167
|
+
if (position === "front") {
|
|
168
|
+
this._items.unshift(item);
|
|
169
|
+
this._itemTimestamps.unshift(Date.now());
|
|
170
|
+
} else {
|
|
171
|
+
this._items.push(item);
|
|
172
|
+
this._itemTimestamps.push(Date.now());
|
|
244
173
|
}
|
|
245
|
-
}
|
|
174
|
+
}
|
|
175
|
+
if (runOnItemsChange) {
|
|
176
|
+
(_d = (_c = this._options).onItemsChange) == null ? void 0 : _d.call(_c, this);
|
|
177
|
+
}
|
|
178
|
+
if (this._running && !this._pendingTick) {
|
|
179
|
+
this._pendingTick = true;
|
|
180
|
+
this.tick();
|
|
181
|
+
}
|
|
246
182
|
}
|
|
247
183
|
/**
|
|
248
|
-
* Removes and returns
|
|
184
|
+
* Removes and returns the next item from the queue without executing the task function.
|
|
185
|
+
* Use for manual queue management. Normally, use execute() to process items.
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* ```ts
|
|
189
|
+
* // FIFO
|
|
190
|
+
* queuer.getNextItem();
|
|
191
|
+
* // LIFO
|
|
192
|
+
* queuer.getNextItem('back');
|
|
193
|
+
* ```
|
|
249
194
|
*/
|
|
250
195
|
getNextItem(position = this._options.getItemsFrom) {
|
|
196
|
+
var _a, _b;
|
|
251
197
|
let item;
|
|
252
198
|
if (position === "front") {
|
|
253
199
|
item = this._items.shift();
|
|
@@ -257,14 +203,85 @@ class AsyncQueuer {
|
|
|
257
203
|
this._itemTimestamps.pop();
|
|
258
204
|
}
|
|
259
205
|
if (item !== void 0) {
|
|
260
|
-
this.
|
|
261
|
-
|
|
262
|
-
|
|
206
|
+
(_b = (_a = this._options).onItemsChange) == null ? void 0 : _b.call(_a, this);
|
|
207
|
+
}
|
|
208
|
+
return item;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Removes and returns the next item from the queue and executes the task function with it.
|
|
212
|
+
*
|
|
213
|
+
* @example
|
|
214
|
+
* ```ts
|
|
215
|
+
* queuer.execute();
|
|
216
|
+
* // LIFO
|
|
217
|
+
* queuer.execute('back');
|
|
218
|
+
* ```
|
|
219
|
+
*/
|
|
220
|
+
async execute(position) {
|
|
221
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
222
|
+
const item = this.getNextItem(position);
|
|
223
|
+
if (item !== void 0) {
|
|
224
|
+
try {
|
|
225
|
+
this._lastResult = await this.fn(item);
|
|
226
|
+
this._successCount++;
|
|
227
|
+
(_b = (_a = this._options).onSuccess) == null ? void 0 : _b.call(_a, this._lastResult, this);
|
|
228
|
+
} catch (error) {
|
|
229
|
+
this._errorCount++;
|
|
230
|
+
(_d = (_c = this._options).onError) == null ? void 0 : _d.call(_c, error, this);
|
|
231
|
+
if (this._options.throwOnError) {
|
|
232
|
+
throw error;
|
|
233
|
+
}
|
|
234
|
+
} finally {
|
|
235
|
+
this._settledCount++;
|
|
236
|
+
this._activeItems.delete(item);
|
|
237
|
+
(_f = (_e = this._options).onItemsChange) == null ? void 0 : _f.call(_e, this);
|
|
238
|
+
(_h = (_g = this._options).onSettled) == null ? void 0 : _h.call(_g, this);
|
|
239
|
+
}
|
|
263
240
|
}
|
|
264
241
|
return item;
|
|
265
242
|
}
|
|
266
243
|
/**
|
|
267
|
-
*
|
|
244
|
+
* Checks for expired items in the queue and removes them. Calls onExpire for each expired item.
|
|
245
|
+
* Internal use only.
|
|
246
|
+
*/
|
|
247
|
+
checkExpiredItems() {
|
|
248
|
+
var _a, _b, _c, _d;
|
|
249
|
+
if (this._options.expirationDuration === Infinity && this._options.getIsExpired === defaultOptions.getIsExpired)
|
|
250
|
+
return;
|
|
251
|
+
const now = Date.now();
|
|
252
|
+
const expiredIndices = [];
|
|
253
|
+
for (let i = 0; i < this._items.length; i++) {
|
|
254
|
+
const timestamp = this._itemTimestamps[i];
|
|
255
|
+
if (timestamp === void 0) continue;
|
|
256
|
+
const item = this._items[i];
|
|
257
|
+
if (item === void 0) continue;
|
|
258
|
+
const isExpired = this._options.getIsExpired !== defaultOptions.getIsExpired ? this._options.getIsExpired(item, timestamp) : now - timestamp > this._options.expirationDuration;
|
|
259
|
+
if (isExpired) {
|
|
260
|
+
expiredIndices.push(i);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
for (let i = expiredIndices.length - 1; i >= 0; i--) {
|
|
264
|
+
const index = expiredIndices[i];
|
|
265
|
+
if (index === void 0) continue;
|
|
266
|
+
const expiredItem = this._items[index];
|
|
267
|
+
if (expiredItem === void 0) continue;
|
|
268
|
+
this._items.splice(index, 1);
|
|
269
|
+
this._itemTimestamps.splice(index, 1);
|
|
270
|
+
this._expirationCount++;
|
|
271
|
+
(_b = (_a = this._options).onExpire) == null ? void 0 : _b.call(_a, expiredItem, this);
|
|
272
|
+
}
|
|
273
|
+
if (expiredIndices.length > 0) {
|
|
274
|
+
(_d = (_c = this._options).onItemsChange) == null ? void 0 : _d.call(_c, this);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Returns the next item in the queue without removing it.
|
|
279
|
+
*
|
|
280
|
+
* @example
|
|
281
|
+
* ```ts
|
|
282
|
+
* queuer.getPeek(); // front
|
|
283
|
+
* queuer.getPeek('back'); // back
|
|
284
|
+
* ```
|
|
268
285
|
*/
|
|
269
286
|
getPeek(position = "front") {
|
|
270
287
|
if (position === "front") {
|
|
@@ -273,106 +290,87 @@ class AsyncQueuer {
|
|
|
273
290
|
return this._items[this._items.length - 1];
|
|
274
291
|
}
|
|
275
292
|
/**
|
|
276
|
-
* Returns true if the
|
|
293
|
+
* Returns true if the queue is empty (no pending items).
|
|
277
294
|
*/
|
|
278
295
|
getIsEmpty() {
|
|
279
296
|
return this._items.length === 0;
|
|
280
297
|
}
|
|
281
298
|
/**
|
|
282
|
-
* Returns true if the
|
|
299
|
+
* Returns true if the queue is full (reached maxSize).
|
|
283
300
|
*/
|
|
284
301
|
getIsFull() {
|
|
285
302
|
return this._items.length >= this._options.maxSize;
|
|
286
303
|
}
|
|
287
304
|
/**
|
|
288
|
-
* Returns the
|
|
305
|
+
* Returns the number of pending items in the queue.
|
|
289
306
|
*/
|
|
290
307
|
getSize() {
|
|
291
308
|
return this._items.length;
|
|
292
309
|
}
|
|
293
310
|
/**
|
|
294
|
-
* Returns a copy of all items in the
|
|
311
|
+
* Returns a copy of all items in the queue, including active and pending items.
|
|
295
312
|
*/
|
|
296
313
|
getAllItems() {
|
|
297
314
|
return [...this.getActiveItems(), ...this.getPendingItems()];
|
|
298
315
|
}
|
|
299
316
|
/**
|
|
300
|
-
* Returns the active
|
|
317
|
+
* Returns the items currently being processed (active tasks).
|
|
301
318
|
*/
|
|
302
319
|
getActiveItems() {
|
|
303
320
|
return Array.from(this._activeItems);
|
|
304
321
|
}
|
|
305
322
|
/**
|
|
306
|
-
* Returns the pending
|
|
323
|
+
* Returns the items waiting to be processed (pending tasks).
|
|
307
324
|
*/
|
|
308
325
|
getPendingItems() {
|
|
309
326
|
return [...this._items];
|
|
310
327
|
}
|
|
311
328
|
/**
|
|
312
|
-
* Returns the number of items that have been
|
|
329
|
+
* Returns the number of items that have been successfully processed.
|
|
313
330
|
*/
|
|
314
|
-
|
|
315
|
-
return this.
|
|
331
|
+
getSuccessCount() {
|
|
332
|
+
return this._successCount;
|
|
316
333
|
}
|
|
317
334
|
/**
|
|
318
|
-
* Returns the number of items that have
|
|
335
|
+
* Returns the number of items that have failed processing.
|
|
319
336
|
*/
|
|
320
|
-
|
|
321
|
-
return this.
|
|
337
|
+
getErrorCount() {
|
|
338
|
+
return this._errorCount;
|
|
322
339
|
}
|
|
323
340
|
/**
|
|
324
|
-
* Returns
|
|
341
|
+
* Returns the number of items that have completed processing (success or error).
|
|
325
342
|
*/
|
|
326
|
-
|
|
327
|
-
return this.
|
|
343
|
+
getSettledCount() {
|
|
344
|
+
return this._settledCount;
|
|
328
345
|
}
|
|
329
346
|
/**
|
|
330
|
-
* Returns
|
|
347
|
+
* Returns the number of items that have been rejected from being added to the queue.
|
|
331
348
|
*/
|
|
332
|
-
|
|
333
|
-
return this.
|
|
334
|
-
}
|
|
335
|
-
/**
|
|
336
|
-
* Adds a callback to be called when a task succeeds
|
|
337
|
-
*/
|
|
338
|
-
onSuccess(cb) {
|
|
339
|
-
this._onSuccessCallbacks.push(cb);
|
|
340
|
-
return () => {
|
|
341
|
-
this._onSuccessCallbacks = this._onSuccessCallbacks.filter(
|
|
342
|
-
(d) => d !== cb
|
|
343
|
-
);
|
|
344
|
-
};
|
|
349
|
+
getRejectionCount() {
|
|
350
|
+
return this._rejectionCount;
|
|
345
351
|
}
|
|
346
352
|
/**
|
|
347
|
-
*
|
|
353
|
+
* Returns true if the queuer is currently running (processing items).
|
|
348
354
|
*/
|
|
349
|
-
|
|
350
|
-
this.
|
|
351
|
-
return () => {
|
|
352
|
-
this._onErrorCallbacks = this._onErrorCallbacks.filter((d) => d !== cb);
|
|
353
|
-
};
|
|
355
|
+
getIsRunning() {
|
|
356
|
+
return this._running;
|
|
354
357
|
}
|
|
355
358
|
/**
|
|
356
|
-
*
|
|
359
|
+
* Returns true if the queuer is running but has no items to process and no active tasks.
|
|
357
360
|
*/
|
|
358
|
-
|
|
359
|
-
this.
|
|
360
|
-
return () => {
|
|
361
|
-
this._onSettledCallbacks = this._onSettledCallbacks.filter(
|
|
362
|
-
(d) => d !== cb
|
|
363
|
-
);
|
|
364
|
-
};
|
|
361
|
+
getIsIdle() {
|
|
362
|
+
return this._running && this.getIsEmpty() && this._activeItems.size === 0;
|
|
365
363
|
}
|
|
366
364
|
/**
|
|
367
|
-
* Returns the number of items that have expired from the
|
|
365
|
+
* Returns the number of items that have expired and been removed from the queue.
|
|
368
366
|
*/
|
|
369
367
|
getExpirationCount() {
|
|
370
368
|
return this._expirationCount;
|
|
371
369
|
}
|
|
372
370
|
}
|
|
373
|
-
function asyncQueue(
|
|
374
|
-
const
|
|
375
|
-
return
|
|
371
|
+
function asyncQueue(fn, initialOptions) {
|
|
372
|
+
const asyncQueuer = new AsyncQueuer(fn, initialOptions);
|
|
373
|
+
return asyncQueuer.addItem.bind(asyncQueuer);
|
|
376
374
|
}
|
|
377
375
|
export {
|
|
378
376
|
AsyncQueuer,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"async-queuer.js","sources":["../../src/async-queuer.ts"],"sourcesContent":["import { parseFunctionOrValue } from './utils'\nimport type { AnyAsyncFunction } from './types'\nimport 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 * Can be a number or a function that returns a number.\n * @default 1\n */\n concurrency?: number | ((queuer: AsyncQueuer<TValue>) => number)\n /**\n * Maximum time in milliseconds that an item can stay in the queue\n * If not provided, items will never expire\n */\n expirationDuration?: number\n /**\n * Function to determine if an item has expired\n * If provided, this overrides the expirationDuration behavior\n */\n getIsExpired?: (item: () => Promise<TValue>, addedAt: number) => boolean\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 * Callback fired whenever an item expires in the queuer\n */\n onExpire?: (item: () => Promise<TValue>, queuer: AsyncQueuer<TValue>) => void\n /**\n * Whether the queuer should start processing tasks immediately or not.\n */\n started?: boolean\n /**\n * Time in milliseconds to wait between processing items.\n * Can be a number or a function that returns a number.\n * @default 0\n */\n wait?: number | ((queuer: AsyncQueuer<TValue>) => number)\n}\n\nconst defaultOptions: Required<AsyncQueuerOptions<any>> = {\n addItemsTo: 'back',\n concurrency: 1,\n expirationDuration: Infinity,\n getIsExpired: () => false,\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 onExpire: () => {},\n started: true,\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 * - Item expiration to clear stale items from the queue\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 _expirationCount = 0\n private _items: Array<() => Promise<TValue>> = []\n private _itemTimestamps: Array<number> = []\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(newOptions: Partial<AsyncQueuerOptions<TValue>>): void {\n this._options = { ...this._options, ...newOptions }\n }\n\n /**\n * Returns the current queuer options\n */\n getOptions(): Required<AsyncQueuerOptions<TValue>> {\n return this._options\n }\n\n /**\n * Returns the current wait time between processing items\n */\n getWait(): number {\n return parseFunctionOrValue(this._options.wait, this)\n }\n\n /**\n * Returns the current concurrency limit\n */\n getConcurrency(): number {\n return parseFunctionOrValue(this._options.concurrency, this)\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 // Check for expired items\n this.checkExpiredItems()\n\n while (\n this._activeItems.size < this.getConcurrency() &&\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 const wait = this.getWait()\n if (wait > 0) {\n setTimeout(() => this.tick(), wait)\n return\n }\n\n this.tick()\n })()\n }\n\n this._pendingTick = false\n }\n\n /**\n * Checks for and removes expired items from the queuer\n */\n private checkExpiredItems() {\n if (\n this._options.expirationDuration === Infinity &&\n this._options.getIsExpired === defaultOptions.getIsExpired\n )\n return\n\n const now = Date.now()\n const expiredIndices: Array<number> = []\n\n // Find indices of expired items\n for (let i = 0; i < this._items.length; i++) {\n const timestamp = this._itemTimestamps[i]\n if (timestamp === undefined) continue\n\n const item = this._items[i]\n if (item === undefined) continue\n\n const isExpired =\n this._options.getIsExpired !== defaultOptions.getIsExpired\n ? this._options.getIsExpired(item, timestamp)\n : now - timestamp > this._options.expirationDuration\n\n if (isExpired) {\n expiredIndices.push(i)\n }\n }\n\n // Remove expired items from back to front to maintain indices\n for (let i = expiredIndices.length - 1; i >= 0; i--) {\n const index = expiredIndices[i]\n if (index === undefined) continue\n\n const expiredItem = this._items[index]\n if (expiredItem === undefined) continue\n\n this._items.splice(index, 1)\n this._itemTimestamps.splice(index, 1)\n this._expirationCount++\n this._options.onExpire(expiredItem, this)\n }\n\n if (expiredIndices.length > 0) {\n this._options.onItemsChange(this)\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.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: AnyAsyncFunction & { priority?: number },\n position: QueuePosition = this._options.addItemsTo,\n runOnItemsChange: 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 this._itemTimestamps.push(Date.now())\n } else {\n this._items.splice(insertIndex, 0, task)\n this._itemTimestamps.splice(insertIndex, 0, Date.now())\n }\n } else {\n // Default FIFO/LIFO behavior\n if (position === 'front') {\n this._items.unshift(task)\n this._itemTimestamps.unshift(Date.now())\n } else {\n this._items.push(task)\n this._itemTimestamps.push(Date.now())\n }\n }\n\n if (runOnItemsChange) {\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 this._itemTimestamps.shift()\n } else {\n item = this._items.pop()\n this._itemTimestamps.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 * Returns the number of items that have expired from the queuer\n */\n getExpirationCount(): number {\n return this._expirationCount\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)\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 { parseFunctionOrValue } from './utils'\nimport type { OptionalKeys } from './types'\nimport 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 * Can be a number or a function that returns a number.\n * @default 1\n */\n concurrency?: number | ((queuer: AsyncQueuer<TValue>) => number)\n /**\n * Maximum time in milliseconds that an item can stay in the queue\n * If not provided, items will never expire\n */\n expirationDuration?: number\n /**\n * Function to determine if an item has expired\n * If provided, this overrides the expirationDuration behavior\n */\n getIsExpired?: (item: TValue, addedAt: number) => boolean\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: TValue) => number\n /**\n * Initial items to populate the queuer with\n */\n initialItems?: Array<TValue>\n /**\n * Maximum number of items allowed in the queuer\n */\n maxSize?: number\n /**\n * Optional error handler for when a task throws.\n * If provided, the handler will be called with the error and queuer instance.\n * This can be used alongside throwOnError - the handler will be called before any error is thrown.\n */\n onError?: (error: unknown, queuer: AsyncQueuer<TValue>) => void\n /**\n * Callback fired whenever an item expires in the queuer\n */\n onExpire?: (item: TValue, queuer: AsyncQueuer<TValue>) => 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: TValue, queuer: AsyncQueuer<TValue>) => void\n /**\n * Optional callback to call when a task is settled\n */\n onSettled?: (queuer: AsyncQueuer<TValue>) => void\n /**\n * Optional callback to call when a task succeeds\n */\n onSuccess?: (result: TValue, queuer: AsyncQueuer<TValue>) => void\n /**\n * Whether the queuer should start processing tasks immediately or not.\n */\n started?: boolean\n /**\n * Whether to throw errors when they occur.\n * Defaults to true if no onError handler is provided, false if an onError handler is provided.\n * Can be explicitly set to override these defaults.\n */\n throwOnError?: boolean\n /**\n * Time in milliseconds to wait between processing items.\n * Can be a number or a function that returns a number.\n * @default 0\n */\n wait?: number | ((queuer: AsyncQueuer<TValue>) => number)\n}\n\ntype AsyncQueuerOptionsWithOptionalCallbacks = OptionalKeys<\n Required<AsyncQueuerOptions<any>>,\n | 'throwOnError'\n | 'onSuccess'\n | 'onSettled'\n | 'onReject'\n | 'onItemsChange'\n | 'onIsRunningChange'\n | 'onExpire'\n | 'onError'\n>\n\nconst defaultOptions: AsyncQueuerOptionsWithOptionalCallbacks = {\n addItemsTo: 'back',\n concurrency: 1,\n expirationDuration: Infinity,\n getIsExpired: () => false,\n getItemsFrom: 'front',\n getPriority: (item: any) => item?.priority ?? 0,\n initialItems: [],\n maxSize: Infinity,\n started: true,\n wait: 0,\n}\n\n/**\n * A flexible asynchronous queue for processing tasks with configurable concurrency, priority, and expiration.\n *\n * Features:\n * - Priority queue support via the getPriority option\n * - Configurable concurrency limit\n * - Callbacks for task success, error, completion, and queue state changes\n * - FIFO (First In First Out) or LIFO (Last In First Out) queue behavior\n * - Pause and resume processing\n * - Task cancellation\n * - Item expiration to remove stale items from the queue\n *\n * Tasks are processed concurrently up to the configured concurrency limit. When a task completes,\n * the next pending task is processed if the concurrency limit allows.\n *\n * Error Handling:\n * - If an `onError` handler is provided, it will be called with the error and queuer instance\n * - If `throwOnError` is true (default when no onError handler is provided), the error will be thrown\n * - If `throwOnError` is false (default when onError handler is provided), the error will be swallowed\n * - Both onError and throwOnError can be used together; the handler will be called before any error is thrown\n * - The error state can be checked using the AsyncQueuer instance\n *\n * Example usage:\n * ```ts\n * const asyncQueuer = new AsyncQueuer<string>(async (item) => {\n * // process item\n * return item.toUpperCase();\n * }, {\n * concurrency: 2,\n * onSuccess: (result) => {\n * console.log(result);\n * }\n * });\n *\n * asyncQueuer.addItem('hello');\n * asyncQueuer.start();\n * ```\n */\nexport class AsyncQueuer<TValue> {\n private _options: AsyncQueuerOptionsWithOptionalCallbacks\n private _activeItems: Set<TValue> = new Set()\n private _successCount = 0\n private _errorCount = 0\n private _settledCount = 0\n private _rejectionCount = 0\n private _expirationCount = 0\n private _items: Array<TValue> = []\n private _itemTimestamps: Array<number> = []\n private _pendingTick = false\n private _running: boolean\n private _lastResult: any\n\n constructor(\n private fn: (value: TValue) => Promise<any>,\n initialOptions: AsyncQueuerOptions<TValue>,\n ) {\n this._options = {\n ...defaultOptions,\n ...initialOptions,\n throwOnError: initialOptions.throwOnError ?? !initialOptions.onError,\n }\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. New options are merged with existing options.\n */\n setOptions(newOptions: Partial<AsyncQueuerOptions<TValue>>): void {\n this._options = { ...this._options, ...newOptions }\n }\n\n /**\n * Returns the current queuer options, including defaults and any overrides.\n */\n getOptions(): AsyncQueuerOptions<TValue> {\n return this._options\n }\n\n /**\n * Returns the current wait time (in milliseconds) between processing items.\n * If a function is provided, it is called with the queuer instance.\n */\n getWait(): number {\n return parseFunctionOrValue(this._options.wait, this)\n }\n\n /**\n * Returns the current concurrency limit for processing items.\n * If a function is provided, it is called with the queuer instance.\n */\n getConcurrency(): number {\n return parseFunctionOrValue(this._options.concurrency, this)\n }\n\n /**\n * Processes items in the queue up to the concurrency limit. Internal use only.\n */\n private tick() {\n if (!this._running) {\n this._pendingTick = false\n return\n }\n\n // Check for expired items\n this.checkExpiredItems()\n\n // Process items concurrently up to the concurrency limit\n while (\n this._activeItems.size < this.getConcurrency() &&\n !this.getIsEmpty()\n ) {\n const nextItem = this.getPeek()\n if (!nextItem) {\n break\n }\n this._activeItems.add(nextItem)\n this._options.onItemsChange?.(this)\n ;(async () => {\n this._lastResult = await this.execute()\n\n const wait = this.getWait()\n if (wait > 0) {\n setTimeout(() => this.tick(), wait)\n return\n }\n\n this.tick()\n })()\n }\n\n this._pendingTick = false\n }\n\n /**\n * Starts processing items in the queue. If already running, does nothing.\n */\n start(): 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\n /**\n * Stops processing items in the queue. Does not clear the queue.\n */\n stop(): void {\n this._running = false\n this._pendingTick = false\n this._options.onIsRunningChange?.(this)\n }\n\n /**\n * Removes all pending items from the queue. Does not affect active tasks.\n */\n clear(): void {\n this._items = []\n this._options.onItemsChange?.(this)\n }\n\n /**\n * Resets the queuer to its initial state. Optionally repopulates with initial items.\n * Does not affect callbacks or options.\n */\n reset(withInitialItems?: boolean): void {\n this.clear()\n this._successCount = 0\n this._errorCount = 0\n this._settledCount = 0\n if (withInitialItems) {\n this._items = [...this._options.initialItems]\n }\n this._running = this._options.started\n }\n\n /**\n * Adds an item to the queue. If the queue is full, the item is rejected and onReject is called.\n * Items can be inserted based on priority or at the front/back depending on configuration.\n *\n * @example\n * ```ts\n * queuer.addItem({ value: 'task', priority: 10 });\n * queuer.addItem('task2', 'front');\n * ```\n */\n addItem(\n item: TValue & { priority?: number },\n position: QueuePosition = this._options.addItemsTo,\n runOnItemsChange: boolean = true,\n ): void {\n if (this.getIsFull()) {\n this._rejectionCount++\n this._options.onReject?.(item, this)\n return\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(item)\n : item.priority\n\n if (priority !== undefined) {\n // Insert based on priority - higher priority items go to front\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(item)\n this._itemTimestamps.push(Date.now())\n } else {\n this._items.splice(insertIndex, 0, item)\n this._itemTimestamps.splice(insertIndex, 0, Date.now())\n }\n } else {\n if (position === 'front') {\n // Default FIFO/LIFO behavior\n this._items.unshift(item)\n this._itemTimestamps.unshift(Date.now())\n } else {\n // LIFO\n this._items.push(item)\n this._itemTimestamps.push(Date.now())\n }\n }\n\n if (runOnItemsChange) {\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 * Removes and returns the next item from the queue without executing the task function.\n * Use for manual queue management. Normally, use execute() to process items.\n *\n * @example\n * ```ts\n * // FIFO\n * queuer.getNextItem();\n * // LIFO\n * queuer.getNextItem('back');\n * ```\n */\n getNextItem(\n position: QueuePosition = this._options.getItemsFrom,\n ): TValue | undefined {\n let item: TValue | undefined\n\n if (position === 'front') {\n item = this._items.shift()\n this._itemTimestamps.shift()\n } else {\n item = this._items.pop()\n this._itemTimestamps.pop()\n }\n\n if (item !== undefined) {\n this._options.onItemsChange?.(this)\n }\n\n return item\n }\n\n /**\n * Removes and returns the next item from the queue and executes the task function with it.\n *\n * @example\n * ```ts\n * queuer.execute();\n * // LIFO\n * queuer.execute('back');\n * ```\n */\n async execute(position?: QueuePosition): Promise<any> {\n const item = this.getNextItem(position)\n if (item !== undefined) {\n try {\n this._lastResult = await this.fn(item)\n this._successCount++\n this._options.onSuccess?.(this._lastResult, this)\n } catch (error) {\n this._errorCount++\n this._options.onError?.(error, this)\n if (this._options.throwOnError) {\n throw error\n }\n } finally {\n this._settledCount++\n this._activeItems.delete(item)\n this._options.onItemsChange?.(this)\n this._options.onSettled?.(this)\n }\n }\n return item\n }\n\n /**\n * Checks for expired items in the queue and removes them. Calls onExpire for each expired item.\n * Internal use only.\n */\n private checkExpiredItems(): void {\n if (\n this._options.expirationDuration === Infinity &&\n this._options.getIsExpired === defaultOptions.getIsExpired\n )\n return\n\n const now = Date.now()\n const expiredIndices: Array<number> = []\n\n // Find indices of expired items\n for (let i = 0; i < this._items.length; i++) {\n const timestamp = this._itemTimestamps[i]\n if (timestamp === undefined) continue\n\n const item = this._items[i]\n if (item === undefined) continue\n\n const isExpired =\n this._options.getIsExpired !== defaultOptions.getIsExpired\n ? this._options.getIsExpired(item, timestamp)\n : now - timestamp > this._options.expirationDuration\n\n if (isExpired) {\n expiredIndices.push(i)\n }\n }\n\n // Remove expired items from back to front to maintain indices\n for (let i = expiredIndices.length - 1; i >= 0; i--) {\n const index = expiredIndices[i]\n if (index === undefined) continue\n\n const expiredItem = this._items[index]\n if (expiredItem === undefined) continue\n\n this._items.splice(index, 1)\n this._itemTimestamps.splice(index, 1)\n this._expirationCount++\n this._options.onExpire?.(expiredItem, this)\n }\n\n if (expiredIndices.length > 0) {\n this._options.onItemsChange?.(this)\n }\n }\n\n /**\n * Returns the next item in the queue without removing it.\n *\n * @example\n * ```ts\n * queuer.getPeek(); // front\n * queuer.getPeek('back'); // back\n * ```\n */\n getPeek(position: QueuePosition = 'front'): 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 queue is empty (no pending items).\n */\n getIsEmpty(): boolean {\n return this._items.length === 0\n }\n\n /**\n * Returns true if the queue is full (reached maxSize).\n */\n getIsFull(): boolean {\n return this._items.length >= this._options.maxSize\n }\n\n /**\n * Returns the number of pending items in the queue.\n */\n getSize(): number {\n return this._items.length\n }\n\n /**\n * Returns a copy of all items in the queue, including active and pending items.\n */\n getAllItems(): Array<TValue> {\n return [...this.getActiveItems(), ...this.getPendingItems()]\n }\n\n /**\n * Returns the items currently being processed (active tasks).\n */\n getActiveItems(): Array<TValue> {\n return Array.from(this._activeItems)\n }\n\n /**\n * Returns the items waiting to be processed (pending tasks).\n */\n getPendingItems(): Array<TValue> {\n return [...this._items]\n }\n\n /**\n * Returns the number of items that have been successfully processed.\n */\n getSuccessCount(): number {\n return this._successCount\n }\n\n /**\n * Returns the number of items that have failed processing.\n */\n getErrorCount(): number {\n return this._errorCount\n }\n\n /**\n * Returns the number of items that have completed processing (success or error).\n */\n getSettledCount(): number {\n return this._settledCount\n }\n\n /**\n * Returns the number of items that have been rejected from being added to the queue.\n */\n getRejectionCount(): number {\n return this._rejectionCount\n }\n\n /**\n * Returns true if the queuer is currently running (processing items).\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 and no active tasks.\n */\n getIsIdle(): boolean {\n return this._running && this.getIsEmpty() && this._activeItems.size === 0\n }\n\n /**\n * Returns the number of items that have expired and been removed from the queue.\n */\n getExpirationCount(): number {\n return this._expirationCount\n }\n}\n\n/**\n * Creates a new AsyncQueuer instance and returns a bound addItem function for adding tasks.\n * The queuer is started automatically and ready to process items.\n *\n * Error Handling:\n * - If an `onError` handler is provided, it will be called with the error and queuer instance\n * - If `throwOnError` is true (default when no onError handler is provided), the error will be thrown\n * - If `throwOnError` is false (default when onError handler is provided), the error will be swallowed\n * - Both onError and throwOnError can be used together; the handler will be called before any error is thrown\n * - The error state can be checked using the underlying AsyncQueuer instance\n *\n * Example usage:\n * ```ts\n * const enqueue = asyncQueue<string>(async (item) => {\n * return item.toUpperCase();\n * }, {...options});\n *\n * enqueue('hello');\n * ```\n */\nexport function asyncQueue<TValue>(\n fn: (value: TValue) => Promise<any>,\n initialOptions: AsyncQueuerOptions<TValue>,\n) {\n const asyncQueuer = new AsyncQueuer<TValue>(fn, initialOptions)\n return asyncQueuer.addItem.bind(asyncQueuer)\n}\n"],"names":[],"mappings":"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"}
|