@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
|
@@ -4,31 +4,36 @@ const defaultOptions = {
|
|
|
4
4
|
addItemsTo: "back",
|
|
5
5
|
concurrency: 1,
|
|
6
6
|
getItemsFrom: "front",
|
|
7
|
-
getPriority: (item) => item.priority ?? 0,
|
|
7
|
+
getPriority: (item) => (item == null ? void 0 : item.priority) ?? 0,
|
|
8
8
|
initialItems: [],
|
|
9
9
|
maxSize: Infinity,
|
|
10
10
|
onGetNextItem: () => {
|
|
11
11
|
},
|
|
12
|
-
|
|
12
|
+
onIsRunningChange: () => {
|
|
13
|
+
},
|
|
14
|
+
onItemsChange: () => {
|
|
15
|
+
},
|
|
16
|
+
onReject: () => {
|
|
13
17
|
},
|
|
14
18
|
started: false,
|
|
15
19
|
wait: 0
|
|
16
20
|
};
|
|
17
21
|
class AsyncQueuer {
|
|
18
22
|
constructor(initialOptions = defaultOptions) {
|
|
19
|
-
this.
|
|
20
|
-
this.
|
|
21
|
-
this.
|
|
22
|
-
this.
|
|
23
|
-
this.
|
|
24
|
-
this.
|
|
25
|
-
this.
|
|
26
|
-
this.
|
|
27
|
-
this.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
23
|
+
this._activeItems = /* @__PURE__ */ new Set();
|
|
24
|
+
this._executionCount = 0;
|
|
25
|
+
this._rejectionCount = 0;
|
|
26
|
+
this._items = [];
|
|
27
|
+
this._onErrorCallbacks = [];
|
|
28
|
+
this._onSettledCallbacks = [];
|
|
29
|
+
this._onSuccessCallbacks = [];
|
|
30
|
+
this._pendingTick = false;
|
|
31
|
+
this._options = { ...defaultOptions, ...initialOptions };
|
|
32
|
+
this._running = this._options.started;
|
|
33
|
+
for (let i = 0; i < this._options.initialItems.length; i++) {
|
|
34
|
+
const item = this._options.initialItems[i];
|
|
35
|
+
const isLast = i === this._options.initialItems.length - 1;
|
|
36
|
+
this.addItem(item, this._options.addItemsTo, isLast);
|
|
32
37
|
}
|
|
33
38
|
}
|
|
34
39
|
/**
|
|
@@ -36,23 +41,30 @@ class AsyncQueuer {
|
|
|
36
41
|
* Returns the new options state
|
|
37
42
|
*/
|
|
38
43
|
setOptions(newOptions) {
|
|
39
|
-
this.
|
|
40
|
-
return this.
|
|
44
|
+
this._options = { ...this._options, ...newOptions };
|
|
45
|
+
return this._options;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Returns the current queuer options
|
|
49
|
+
*/
|
|
50
|
+
getOptions() {
|
|
51
|
+
return this._options;
|
|
41
52
|
}
|
|
42
53
|
/**
|
|
43
54
|
* Processes items in the queuer
|
|
44
55
|
*/
|
|
45
56
|
tick() {
|
|
46
|
-
if (!this.
|
|
47
|
-
this.
|
|
57
|
+
if (!this._running) {
|
|
58
|
+
this._pendingTick = false;
|
|
48
59
|
return;
|
|
49
60
|
}
|
|
50
|
-
while (this.
|
|
61
|
+
while (this._activeItems.size < this._options.concurrency && !this.getIsEmpty()) {
|
|
51
62
|
const nextFn = this.getNextItem();
|
|
52
63
|
if (!nextFn) {
|
|
53
64
|
break;
|
|
54
65
|
}
|
|
55
|
-
this.
|
|
66
|
+
this._activeItems.add(nextFn);
|
|
67
|
+
this._options.onItemsChange(this);
|
|
56
68
|
(async () => {
|
|
57
69
|
let success = false;
|
|
58
70
|
let res;
|
|
@@ -63,29 +75,78 @@ class AsyncQueuer {
|
|
|
63
75
|
} catch (e) {
|
|
64
76
|
error = e;
|
|
65
77
|
} finally {
|
|
66
|
-
this.
|
|
78
|
+
this._activeItems.delete(nextFn);
|
|
79
|
+
this._options.onItemsChange(this);
|
|
67
80
|
}
|
|
68
|
-
this.activeItems.delete(nextFn);
|
|
69
81
|
if (success) {
|
|
70
|
-
this.
|
|
82
|
+
this._onSuccessCallbacks.forEach((cb) => cb(res));
|
|
71
83
|
} else {
|
|
72
|
-
this.
|
|
84
|
+
this._onErrorCallbacks.forEach((cb) => cb(error));
|
|
73
85
|
}
|
|
74
|
-
this.
|
|
75
|
-
if (this.
|
|
76
|
-
setTimeout(() => this.tick(), this.
|
|
86
|
+
this._onSettledCallbacks.forEach((cb) => cb(success ? res : error));
|
|
87
|
+
if (this._options.wait > 0) {
|
|
88
|
+
setTimeout(() => this.tick(), this._options.wait);
|
|
77
89
|
return;
|
|
78
90
|
}
|
|
79
91
|
this.tick();
|
|
80
92
|
})();
|
|
81
93
|
}
|
|
82
|
-
this.
|
|
94
|
+
this._pendingTick = false;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Starts the queuer and processes items
|
|
98
|
+
*/
|
|
99
|
+
start() {
|
|
100
|
+
this._running = true;
|
|
101
|
+
if (!this._pendingTick && !this.getIsEmpty()) {
|
|
102
|
+
this._pendingTick = true;
|
|
103
|
+
this.tick();
|
|
104
|
+
}
|
|
105
|
+
this._options.onIsRunningChange(this);
|
|
106
|
+
return new Promise((resolve) => {
|
|
107
|
+
const checkIdle = () => {
|
|
108
|
+
if (this.getIsIdle()) {
|
|
109
|
+
resolve();
|
|
110
|
+
} else {
|
|
111
|
+
setTimeout(checkIdle, 100);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
checkIdle();
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Stops the queuer from processing items
|
|
119
|
+
*/
|
|
120
|
+
stop() {
|
|
121
|
+
this._running = false;
|
|
122
|
+
this._pendingTick = false;
|
|
123
|
+
this._options.onIsRunningChange(this);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Removes all items from the queuer
|
|
127
|
+
*/
|
|
128
|
+
clear() {
|
|
129
|
+
this._items = [];
|
|
130
|
+
this._options.onItemsChange(this);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Resets the queuer to its initial state
|
|
134
|
+
*/
|
|
135
|
+
reset(withInitialItems) {
|
|
136
|
+
this.clear();
|
|
137
|
+
this._executionCount = 0;
|
|
138
|
+
if (withInitialItems) {
|
|
139
|
+
this._items = [...this._options.initialItems];
|
|
140
|
+
}
|
|
141
|
+
this._running = this._options.started;
|
|
83
142
|
}
|
|
84
143
|
/**
|
|
85
144
|
* Adds a task to the queuer
|
|
86
145
|
*/
|
|
87
|
-
addItem(fn, position = this.
|
|
88
|
-
if (this.
|
|
146
|
+
addItem(fn, position = this._options.addItemsTo, runOnUpdate = true) {
|
|
147
|
+
if (this.getIsFull()) {
|
|
148
|
+
this._rejectionCount++;
|
|
149
|
+
this._options.onReject(fn, this);
|
|
89
150
|
return Promise.reject(new Error("Queuer is full"));
|
|
90
151
|
}
|
|
91
152
|
return new Promise((resolve, reject) => {
|
|
@@ -102,29 +163,29 @@ class AsyncQueuer {
|
|
|
102
163
|
},
|
|
103
164
|
{ priority: fn.priority ?? void 0 }
|
|
104
165
|
);
|
|
105
|
-
const priority = this.
|
|
166
|
+
const priority = this._options.getPriority !== defaultOptions.getPriority ? this._options.getPriority(task) : task.priority;
|
|
106
167
|
if (priority !== void 0) {
|
|
107
|
-
const insertIndex = this.
|
|
108
|
-
const existingPriority = this.
|
|
168
|
+
const insertIndex = this._items.findIndex((existing) => {
|
|
169
|
+
const existingPriority = this._options.getPriority !== defaultOptions.getPriority ? this._options.getPriority(existing) : existing.priority;
|
|
109
170
|
return existingPriority > priority;
|
|
110
171
|
});
|
|
111
172
|
if (insertIndex === -1) {
|
|
112
|
-
this.
|
|
173
|
+
this._items.push(task);
|
|
113
174
|
} else {
|
|
114
|
-
this.
|
|
175
|
+
this._items.splice(insertIndex, 0, task);
|
|
115
176
|
}
|
|
116
177
|
} else {
|
|
117
178
|
if (position === "front") {
|
|
118
|
-
this.
|
|
179
|
+
this._items.unshift(task);
|
|
119
180
|
} else {
|
|
120
|
-
this.
|
|
181
|
+
this._items.push(task);
|
|
121
182
|
}
|
|
122
183
|
}
|
|
123
184
|
if (runOnUpdate) {
|
|
124
|
-
this.
|
|
185
|
+
this._options.onItemsChange(this);
|
|
125
186
|
}
|
|
126
|
-
if (this.
|
|
127
|
-
this.
|
|
187
|
+
if (this._running && !this._pendingTick) {
|
|
188
|
+
this._pendingTick = true;
|
|
128
189
|
this.tick();
|
|
129
190
|
}
|
|
130
191
|
});
|
|
@@ -132,157 +193,120 @@ class AsyncQueuer {
|
|
|
132
193
|
/**
|
|
133
194
|
* Removes and returns an item from the queuer
|
|
134
195
|
*/
|
|
135
|
-
getNextItem(position = this.
|
|
196
|
+
getNextItem(position = this._options.getItemsFrom) {
|
|
136
197
|
let item;
|
|
137
198
|
if (position === "front") {
|
|
138
|
-
item = this.
|
|
199
|
+
item = this._items.shift();
|
|
139
200
|
} else {
|
|
140
|
-
item = this.
|
|
201
|
+
item = this._items.pop();
|
|
141
202
|
}
|
|
142
203
|
if (item !== void 0) {
|
|
143
|
-
this.
|
|
144
|
-
this.
|
|
145
|
-
this.
|
|
204
|
+
this._executionCount++;
|
|
205
|
+
this._options.onItemsChange(this);
|
|
206
|
+
this._options.onGetNextItem(item, this);
|
|
146
207
|
}
|
|
147
208
|
return item;
|
|
148
209
|
}
|
|
149
210
|
/**
|
|
150
211
|
* Returns an item without removing it
|
|
151
212
|
*/
|
|
152
|
-
|
|
213
|
+
getPeek(position = "front") {
|
|
153
214
|
if (position === "front") {
|
|
154
|
-
return this.
|
|
215
|
+
return this._items[0];
|
|
155
216
|
}
|
|
156
|
-
return this.
|
|
217
|
+
return this._items[this._items.length - 1];
|
|
157
218
|
}
|
|
158
219
|
/**
|
|
159
220
|
* Returns true if the queuer is empty
|
|
160
221
|
*/
|
|
161
|
-
|
|
162
|
-
return this.
|
|
222
|
+
getIsEmpty() {
|
|
223
|
+
return this._items.length === 0;
|
|
163
224
|
}
|
|
164
225
|
/**
|
|
165
226
|
* Returns true if the queuer is full
|
|
166
227
|
*/
|
|
167
|
-
|
|
168
|
-
return this.
|
|
228
|
+
getIsFull() {
|
|
229
|
+
return this._items.length >= this._options.maxSize;
|
|
169
230
|
}
|
|
170
231
|
/**
|
|
171
232
|
* Returns the current size of the queuer
|
|
172
233
|
*/
|
|
173
|
-
|
|
174
|
-
return this.
|
|
234
|
+
getSize() {
|
|
235
|
+
return this._items.length;
|
|
175
236
|
}
|
|
176
237
|
/**
|
|
177
|
-
*
|
|
238
|
+
* Returns a copy of all items in the queuer
|
|
178
239
|
*/
|
|
179
|
-
|
|
180
|
-
this.
|
|
181
|
-
this.options.onUpdate(this);
|
|
240
|
+
getAllItems() {
|
|
241
|
+
return [...this.getActiveItems(), ...this.getPendingItems()];
|
|
182
242
|
}
|
|
183
243
|
/**
|
|
184
|
-
*
|
|
244
|
+
* Returns the active items
|
|
185
245
|
*/
|
|
186
|
-
|
|
187
|
-
this.
|
|
188
|
-
this.executionCount = 0;
|
|
189
|
-
if (withInitialItems) {
|
|
190
|
-
this.items = [...this.options.initialItems];
|
|
191
|
-
}
|
|
192
|
-
this.running = this.options.started;
|
|
246
|
+
getActiveItems() {
|
|
247
|
+
return Array.from(this._activeItems);
|
|
193
248
|
}
|
|
194
249
|
/**
|
|
195
|
-
* Returns
|
|
250
|
+
* Returns the pending items
|
|
196
251
|
*/
|
|
197
|
-
|
|
198
|
-
return [...this.
|
|
252
|
+
getPendingItems() {
|
|
253
|
+
return [...this._items];
|
|
199
254
|
}
|
|
200
255
|
/**
|
|
201
256
|
* Returns the number of items that have been removed from the queuer
|
|
202
257
|
*/
|
|
203
258
|
getExecutionCount() {
|
|
204
|
-
return this.
|
|
259
|
+
return this._executionCount;
|
|
205
260
|
}
|
|
206
261
|
/**
|
|
207
|
-
* Returns the
|
|
262
|
+
* Returns the number of items that have been rejected from the queuer
|
|
208
263
|
*/
|
|
209
|
-
|
|
210
|
-
return
|
|
264
|
+
getRejectionCount() {
|
|
265
|
+
return this._rejectionCount;
|
|
211
266
|
}
|
|
212
267
|
/**
|
|
213
|
-
* Returns the
|
|
268
|
+
* Returns true if the queuer is running
|
|
214
269
|
*/
|
|
215
|
-
|
|
216
|
-
return this.
|
|
270
|
+
getIsRunning() {
|
|
271
|
+
return this._running;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Returns true if the queuer is running but has no items to process
|
|
275
|
+
*/
|
|
276
|
+
getIsIdle() {
|
|
277
|
+
return this._running && this.getIsEmpty() && this._activeItems.size === 0;
|
|
217
278
|
}
|
|
218
279
|
/**
|
|
219
280
|
* Adds a callback to be called when a task succeeds
|
|
220
281
|
*/
|
|
221
282
|
onSuccess(cb) {
|
|
222
|
-
this.
|
|
283
|
+
this._onSuccessCallbacks.push(cb);
|
|
223
284
|
return () => {
|
|
224
|
-
this.
|
|
285
|
+
this._onSuccessCallbacks = this._onSuccessCallbacks.filter(
|
|
286
|
+
(d) => d !== cb
|
|
287
|
+
);
|
|
225
288
|
};
|
|
226
289
|
}
|
|
227
290
|
/**
|
|
228
291
|
* Adds a callback to be called when a task errors
|
|
229
292
|
*/
|
|
230
293
|
onError(cb) {
|
|
231
|
-
this.
|
|
294
|
+
this._onErrorCallbacks.push(cb);
|
|
232
295
|
return () => {
|
|
233
|
-
this.
|
|
296
|
+
this._onErrorCallbacks = this._onErrorCallbacks.filter((d) => d !== cb);
|
|
234
297
|
};
|
|
235
298
|
}
|
|
236
299
|
/**
|
|
237
300
|
* Adds a callback to be called when a task is settled
|
|
238
301
|
*/
|
|
239
302
|
onSettled(cb) {
|
|
240
|
-
this.
|
|
303
|
+
this._onSettledCallbacks.push(cb);
|
|
241
304
|
return () => {
|
|
242
|
-
this.
|
|
305
|
+
this._onSettledCallbacks = this._onSettledCallbacks.filter(
|
|
306
|
+
(d) => d !== cb
|
|
307
|
+
);
|
|
243
308
|
};
|
|
244
309
|
}
|
|
245
|
-
/**
|
|
246
|
-
* Starts the queuer and processes items
|
|
247
|
-
*/
|
|
248
|
-
start() {
|
|
249
|
-
this.running = true;
|
|
250
|
-
if (!this.pendingTick && !this.isEmpty()) {
|
|
251
|
-
this.pendingTick = true;
|
|
252
|
-
this.tick();
|
|
253
|
-
}
|
|
254
|
-
this.options.onUpdate(this);
|
|
255
|
-
return new Promise((resolve) => {
|
|
256
|
-
const checkIdle = () => {
|
|
257
|
-
if (this.isIdle()) {
|
|
258
|
-
resolve();
|
|
259
|
-
} else {
|
|
260
|
-
setTimeout(checkIdle, 100);
|
|
261
|
-
}
|
|
262
|
-
};
|
|
263
|
-
checkIdle();
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
/**
|
|
267
|
-
* Stops the queuer from processing items
|
|
268
|
-
*/
|
|
269
|
-
stop() {
|
|
270
|
-
this.running = false;
|
|
271
|
-
this.pendingTick = false;
|
|
272
|
-
this.options.onUpdate(this);
|
|
273
|
-
}
|
|
274
|
-
/**
|
|
275
|
-
* Returns true if the queuer is running
|
|
276
|
-
*/
|
|
277
|
-
isRunning() {
|
|
278
|
-
return this.running;
|
|
279
|
-
}
|
|
280
|
-
/**
|
|
281
|
-
* Returns true if the queuer is running but has no items to process
|
|
282
|
-
*/
|
|
283
|
-
isIdle() {
|
|
284
|
-
return this.running && this.isEmpty() && this.activeItems.size === 0;
|
|
285
|
-
}
|
|
286
310
|
}
|
|
287
311
|
function asyncQueue(options = {}) {
|
|
288
312
|
const queuer = new AsyncQueuer({ ...options, started: true });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"async-queuer.cjs","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":";;AAoDA,MAAM,iBAAoD;AAAA,EACxD,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,cAAc;AAAA,EACd,aAAa,CAAC,SAAU,KAAa,YAAY;AAAA,EACjD,cAAc,CAAC;AAAA,EACf,SAAS;AAAA,EACT,eAAe,MAAM;AAAA,EAAC;AAAA,EACtB,UAAU,MAAM;AAAA,EAAC;AAAA,EACjB,SAAS;AAAA,EACT,MAAM;AACR;AA+BO,MAAM,YAAoB;AAAA,EAW/B,YAAY,iBAA6C,gBAAgB;AATzE,SAAQ,QAAsC,CAAC;AACvC,SAAA,kCAA8C,IAAI;AAC1D,SAAQ,qBAAsD,CAAC;AAC/D,SAAQ,mBAAkD,CAAC;AAC3D,SAAQ,qBAA8D,CAAC;AAEvE,SAAQ,cAAc;AACtB,SAAQ,iBAAiB;AAGvB,SAAK,UAAU,EAAE,GAAG,gBAAgB,GAAG,eAAe;AACjD,SAAA,UAAU,KAAK,QAAQ;AAE5B,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,aAAa,QAAQ,KAAK;AACzD,YAAM,OAAO,KAAK,QAAQ,aAAa,CAAC;AACxC,YAAM,SAAS,MAAM,KAAK,QAAQ,aAAa,SAAS;AACxD,WAAK,QAAQ,MAAM,KAAK,QAAQ,YAAY,MAAM;AAAA,IAAA;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA,EAOF,WACE,YAC4B;AAC5B,SAAK,UAAU,EAAE,GAAG,KAAK,SAAS,GAAG,WAAW;AAChD,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMJ,OAAO;AACX,QAAA,CAAC,KAAK,SAAS;AACjB,WAAK,cAAc;AACnB;AAAA,IAAA;AAIA,WAAA,KAAK,YAAY,OAAO,KAAK,QAAQ,eACrC,CAAC,KAAK,WACN;AACM,YAAA,SAAS,KAAK,YAAY;AAChC,UAAI,CAAC,QAAQ;AACX;AAAA,MAAA;AAGG,WAAA,YAAY,IAAI,MAAM;AAC1B,OAAC,YAAY;AACZ,YAAI,UAAU;AACV,YAAA;AACA,YAAA;AAEA,YAAA;AACF,gBAAM,MAAM,OAAO;AACT,oBAAA;AAAA,iBACH,GAAG;AACF,kBAAA;AAAA,QAAA,UACR;AACK,eAAA,QAAQ,SAAS,IAAI;AAAA,QAAA;AAGvB,aAAA,YAAY,OAAO,MAAM;AAC9B,YAAI,SAAS;AACX,eAAK,mBAAmB,QAAQ,CAAC,OAAO,GAAG,GAAG,CAAC;AAAA,QAAA,OAC1C;AACL,eAAK,iBAAiB,QAAQ,CAAC,OAAO,GAAG,KAAM,CAAC;AAAA,QAAA;AAE7C,aAAA,mBAAmB,QAAQ,CAAC,OAAO,GAAG,UAAU,MAAM,KAAM,CAAC;AAE9D,YAAA,KAAK,QAAQ,OAAO,GAAG;AACzB,qBAAW,MAAM,KAAK,KAAQ,GAAA,KAAK,QAAQ,IAAI;AAC/C;AAAA,QAAA;AAGF,aAAK,KAAK;AAAA,MAAA,GACT;AAAA,IAAA;AAGL,SAAK,cAAc;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMrB,QACE,IACA,WAA0B,KAAK,QAAQ,YACvC,cAAuB,MACN;AACb,QAAA,KAAK,UAAU;AACjB,aAAO,QAAQ,OAAO,IAAI,MAAM,gBAAgB,CAAC;AAAA,IAAA;AAGnD,WAAO,IAAI,QAAgB,CAAC,SAAS,WAAW;AAC9C,YAAM,OAAO,OAAO;AAAA,QAClB,YAAY;AACN,cAAA;AACI,kBAAA,SAAS,MAAM,GAAG;AACxB,oBAAQ,MAAM;AACP,mBAAA;AAAA,mBACA,OAAO;AACd,mBAAO,KAAK;AACN,kBAAA;AAAA,UAAA;AAAA,QAEV;AAAA,QACA,EAAE,UAAU,GAAG,YAAY,OAAU;AAAA,MACvC;AAGM,YAAA,WACJ,KAAK,QAAQ,gBAAgB,eAAe,cACxC,KAAK,QAAQ,YAAY,IAAI,IAC7B,KAAK;AAEX,UAAI,aAAa,QAAW;AAE1B,cAAM,cAAc,KAAK,MAAM,UAAU,CAAC,aAAa;AAC/C,gBAAA,mBACJ,KAAK,QAAQ,gBAAgB,eAAe,cACxC,KAAK,QAAQ,YAAY,QAAQ,IAChC,SAAiB;AACxB,iBAAO,mBAAmB;AAAA,QAAA,CAC3B;AAED,YAAI,gBAAgB,IAAI;AACjB,eAAA,MAAM,KAAK,IAAI;AAAA,QAAA,OACf;AACL,eAAK,MAAM,OAAO,aAAa,GAAG,IAAI;AAAA,QAAA;AAAA,MACxC,OACK;AAEL,YAAI,aAAa,SAAS;AACnB,eAAA,MAAM,QAAQ,IAAI;AAAA,QAAA,OAClB;AACA,eAAA,MAAM,KAAK,IAAI;AAAA,QAAA;AAAA,MACtB;AAGF,UAAI,aAAa;AACV,aAAA,QAAQ,SAAS,IAAI;AAAA,MAAA;AAG5B,UAAI,KAAK,WAAW,CAAC,KAAK,aAAa;AACrC,aAAK,cAAc;AACnB,aAAK,KAAK;AAAA,MAAA;AAAA,IACZ,CACD;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMH,YACE,WAA0B,KAAK,QAAQ,cACF;AACjC,QAAA;AAEJ,QAAI,aAAa,SAAS;AACjB,aAAA,KAAK,MAAM,MAAM;AAAA,IAAA,OACnB;AACE,aAAA,KAAK,MAAM,IAAI;AAAA,IAAA;AAGxB,QAAI,SAAS,QAAW;AACjB,WAAA;AACA,WAAA,QAAQ,SAAS,IAAI;AACrB,WAAA,QAAQ,cAAc,MAAM,IAAI;AAAA,IAAA;AAEhC,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMT,KAAK,WAA0B,SAA8C;AAC3E,QAAI,aAAa,SAAS;AACjB,aAAA,KAAK,MAAM,CAAC;AAAA,IAAA;AAErB,WAAO,KAAK,MAAM,KAAK,MAAM,SAAS,CAAC;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMzC,UAAmB;AACV,WAAA,KAAK,MAAM,WAAW;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAM/B,SAAkB;AAChB,WAAO,KAAK,MAAM,UAAU,KAAK,QAAQ;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAM3C,OAAe;AACb,WAAO,KAAK,MAAM;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMpB,QAAc;AACZ,SAAK,QAAQ,CAAC;AACT,SAAA,QAAQ,SAAS,IAAI;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAM5B,MAAM,kBAAkC;AACtC,SAAK,MAAM;AACX,SAAK,iBAAiB;AACtB,QAAI,kBAAkB;AACpB,WAAK,QAAQ,CAAC,GAAG,KAAK,QAAQ,YAAY;AAAA,IAAA;AAEvC,SAAA,UAAU,KAAK,QAAQ;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAM9B,cAA4C;AACnC,WAAA,CAAC,GAAG,KAAK,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMvB,oBAA4B;AAC1B,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMd,iBAA+C;AACtC,WAAA,MAAM,KAAK,KAAK,WAAW;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMpC,kBAAgD;AAC9C,WAAO,KAAK,YAAY;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAM1B,UAAU,IAA8B;AACjC,SAAA,mBAAmB,KAAK,EAAE;AAC/B,WAAO,MAAM;AACX,WAAK,qBAAqB,KAAK,mBAAmB,OAAO,CAAC,MAAM,MAAM,EAAE;AAAA,IAC1E;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMF,QAAQ,IAA4B;AAC7B,SAAA,iBAAiB,KAAK,EAAE;AAC7B,WAAO,MAAM;AACX,WAAK,mBAAmB,KAAK,iBAAiB,OAAO,CAAC,MAAM,MAAM,EAAE;AAAA,IACtE;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMF,UAAU,IAAsC;AACzC,SAAA,mBAAmB,KAAK,EAAE;AAC/B,WAAO,MAAM;AACX,WAAK,qBAAqB,KAAK,mBAAmB,OAAO,CAAC,MAAM,MAAM,EAAE;AAAA,IAC1E;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMF,QAAuB;AACrB,SAAK,UAAU;AACf,QAAI,CAAC,KAAK,eAAe,CAAC,KAAK,WAAW;AACxC,WAAK,cAAc;AACnB,WAAK,KAAK;AAAA,IAAA;AAEP,SAAA,QAAQ,SAAS,IAAI;AAEnB,WAAA,IAAI,QAAc,CAAC,YAAY;AACpC,YAAM,YAAY,MAAM;AAClB,YAAA,KAAK,UAAU;AACT,kBAAA;AAAA,QAAA,OACH;AACL,qBAAW,WAAW,GAAG;AAAA,QAAA;AAAA,MAE7B;AACU,gBAAA;AAAA,IAAA,CACX;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMH,OAAa;AACX,SAAK,UAAU;AACf,SAAK,cAAc;AACd,SAAA,QAAQ,SAAS,IAAI;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAM5B,YAAqB;AACnB,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMd,SAAkB;AAChB,WAAO,KAAK,WAAW,KAAK,QAAa,KAAA,KAAK,YAAY,SAAS;AAAA,EAAA;AAEvE;AAmBgB,SAAA,WAAmB,UAAsC,IAAI;AACrE,QAAA,SAAS,IAAI,YAAoB,EAAE,GAAG,SAAS,SAAS,MAAM;AAC7D,SAAA,OAAO,QAAQ,KAAK,MAAM;AACnC;;;"}
|
|
1
|
+
{"version":3,"file":"async-queuer.cjs","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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"}
|