async-flex-loop 1.0.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/README.md +368 -0
- package/dist/index.cjs +450 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +282 -0
- package/dist/index.d.ts +282 -0
- package/dist/index.js +440 -0
- package/dist/index.js.map +1 -0
- package/package.json +39 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
|
|
5
|
+
// src/errors.ts
|
|
6
|
+
var TimeoutError = class extends Error {
|
|
7
|
+
constructor(item, index, timeoutMs) {
|
|
8
|
+
super(`Task at index ${index} timed out after ${timeoutMs}ms`);
|
|
9
|
+
this.item = item;
|
|
10
|
+
this.index = index;
|
|
11
|
+
this.timeoutMs = timeoutMs;
|
|
12
|
+
this.name = "TimeoutError";
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
var MaxRetryError = class extends Error {
|
|
16
|
+
constructor(item, index, retryCount, originalError) {
|
|
17
|
+
super(`Task at index ${index} failed after ${retryCount} retries: ${originalError.message}`);
|
|
18
|
+
this.item = item;
|
|
19
|
+
this.index = index;
|
|
20
|
+
this.retryCount = retryCount;
|
|
21
|
+
this.originalError = originalError;
|
|
22
|
+
this.name = "MaxRetryError";
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
var QueueAbortError = class extends Error {
|
|
26
|
+
constructor(message = "Queue was aborted") {
|
|
27
|
+
super(message);
|
|
28
|
+
this.name = "QueueAbortError";
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// src/utils/delay.ts
|
|
33
|
+
var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
34
|
+
var yieldToEventLoop = () => new Promise((resolve) => setTimeout(resolve, 0));
|
|
35
|
+
var calculateRetryDelay = (baseDelay, backoff, attemptNumber) => {
|
|
36
|
+
return baseDelay * Math.pow(backoff, attemptNumber - 1);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// src/utils/timeout.ts
|
|
40
|
+
var withTimeout = (promise, ms, item, index) => {
|
|
41
|
+
let timeoutId;
|
|
42
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
43
|
+
timeoutId = setTimeout(() => {
|
|
44
|
+
reject(new TimeoutError(item, index, ms));
|
|
45
|
+
}, ms);
|
|
46
|
+
});
|
|
47
|
+
return Promise.race([promise.finally(() => clearTimeout(timeoutId)), timeoutPromise]);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// src/enums.ts
|
|
51
|
+
var QueueState = /* @__PURE__ */ ((QueueState2) => {
|
|
52
|
+
QueueState2["Idle"] = "idle";
|
|
53
|
+
QueueState2["Pending"] = "pending";
|
|
54
|
+
QueueState2["Processing"] = "processing";
|
|
55
|
+
QueueState2["Paused"] = "paused";
|
|
56
|
+
return QueueState2;
|
|
57
|
+
})(QueueState || {});
|
|
58
|
+
|
|
59
|
+
// src/AsyncFlexLoop.ts
|
|
60
|
+
var AsyncFlexLoop = class {
|
|
61
|
+
/**
|
|
62
|
+
* Constructor with required parameters
|
|
63
|
+
*/
|
|
64
|
+
constructor(initialItems, onItem, options) {
|
|
65
|
+
// ========== Private State ==========
|
|
66
|
+
__publicField(this, "state", "pending" /* Pending */);
|
|
67
|
+
__publicField(this, "queue", []);
|
|
68
|
+
__publicField(this, "activeCount", 0);
|
|
69
|
+
__publicField(this, "nextIndex", 0);
|
|
70
|
+
// Results storage
|
|
71
|
+
__publicField(this, "results", /* @__PURE__ */ new Map());
|
|
72
|
+
// Promises for waiting
|
|
73
|
+
__publicField(this, "idleResolvers", []);
|
|
74
|
+
__publicField(this, "idleRejectors", []);
|
|
75
|
+
// Statistics
|
|
76
|
+
__publicField(this, "stats", {
|
|
77
|
+
totalProcessed: 0,
|
|
78
|
+
totalSuccess: 0,
|
|
79
|
+
totalFailed: 0,
|
|
80
|
+
totalProcessingTime: 0,
|
|
81
|
+
successRate: 0
|
|
82
|
+
});
|
|
83
|
+
// ========== Handler & Options ==========
|
|
84
|
+
__publicField(this, "onItemHandler");
|
|
85
|
+
__publicField(this, "options");
|
|
86
|
+
this.onItemHandler = onItem;
|
|
87
|
+
this.options = {
|
|
88
|
+
concurrency: options?.concurrency ?? Infinity,
|
|
89
|
+
autoStart: options?.autoStart ?? true,
|
|
90
|
+
retry: options?.retry ?? 0,
|
|
91
|
+
retryDelay: options?.retryDelay ?? 0,
|
|
92
|
+
retryBackoff: options?.retryBackoff ?? 1,
|
|
93
|
+
delayAfterTask: options?.delayAfterTask ?? 0,
|
|
94
|
+
timeout: options?.timeout,
|
|
95
|
+
yieldLoop: options?.yieldLoop ?? true,
|
|
96
|
+
throwOnError: options?.throwOnError ?? true,
|
|
97
|
+
onError: options?.onError,
|
|
98
|
+
onTaskComplete: options?.onTaskComplete,
|
|
99
|
+
onProgress: options?.onProgress,
|
|
100
|
+
onIdle: options?.onIdle
|
|
101
|
+
};
|
|
102
|
+
if (initialItems.length > 0) {
|
|
103
|
+
this.push(...initialItems);
|
|
104
|
+
}
|
|
105
|
+
if (this.options.autoStart) {
|
|
106
|
+
this.start();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// ========== Public Methods ==========
|
|
110
|
+
/**
|
|
111
|
+
* Add items to the queue
|
|
112
|
+
* @returns Array of assigned indices
|
|
113
|
+
*/
|
|
114
|
+
push(...items) {
|
|
115
|
+
const indices = [];
|
|
116
|
+
for (const item of items) {
|
|
117
|
+
const queueItem = {
|
|
118
|
+
item,
|
|
119
|
+
index: this.nextIndex,
|
|
120
|
+
retryCount: 0,
|
|
121
|
+
addedAt: Date.now()
|
|
122
|
+
};
|
|
123
|
+
this.queue.push(queueItem);
|
|
124
|
+
indices.push(this.nextIndex);
|
|
125
|
+
this.nextIndex++;
|
|
126
|
+
}
|
|
127
|
+
if (this.state === "idle" /* Idle */) {
|
|
128
|
+
this.start();
|
|
129
|
+
} else if (this.state === "processing" /* Processing */) {
|
|
130
|
+
this.processNext();
|
|
131
|
+
}
|
|
132
|
+
return indices;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Start processing the queue
|
|
136
|
+
*/
|
|
137
|
+
start() {
|
|
138
|
+
if (this.state === "processing" /* Processing */) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (this.state === "paused" /* Paused */) {
|
|
142
|
+
this.resume();
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
this.state = "processing" /* Processing */;
|
|
146
|
+
this.processNext();
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Pause processing (currently running tasks will finish)
|
|
150
|
+
*/
|
|
151
|
+
pause() {
|
|
152
|
+
this.state = "paused" /* Paused */;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Resume processing after a pause
|
|
156
|
+
*/
|
|
157
|
+
resume() {
|
|
158
|
+
if (this.state === "paused" /* Paused */) {
|
|
159
|
+
this.state = "processing" /* Processing */;
|
|
160
|
+
this.processNext();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Clear all remaining items in the queue
|
|
165
|
+
*/
|
|
166
|
+
clear() {
|
|
167
|
+
this.queue = [];
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Wait until the queue is empty and all tasks have completed
|
|
171
|
+
*/
|
|
172
|
+
onIdle() {
|
|
173
|
+
if (this.state === "idle" /* Idle */ || this.queue.length === 0 && this.activeCount === 0) {
|
|
174
|
+
return Promise.resolve();
|
|
175
|
+
}
|
|
176
|
+
return new Promise((resolve, reject) => {
|
|
177
|
+
this.idleResolvers.push(resolve);
|
|
178
|
+
this.idleRejectors.push(reject);
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
getNextItem() {
|
|
182
|
+
return this.queue[0]?.item;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Get results (waits for queue to finish)
|
|
186
|
+
* @returns Array with undefined for failed tasks
|
|
187
|
+
*/
|
|
188
|
+
async getResults() {
|
|
189
|
+
await this.onIdle();
|
|
190
|
+
return this.buildResultsArray();
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Get raw results with full information (waits for queue to finish)
|
|
194
|
+
*/
|
|
195
|
+
async getRawResults() {
|
|
196
|
+
await this.onIdle();
|
|
197
|
+
return Array.from(this.results.values()).sort((a, b) => a.index - b.index);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Get only successful results (waits for queue to finish)
|
|
201
|
+
*/
|
|
202
|
+
async getCompletedResultsAsync() {
|
|
203
|
+
await this.onIdle();
|
|
204
|
+
return this.getCompletedResults();
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Get current successful results (does not wait)
|
|
208
|
+
*/
|
|
209
|
+
getCompletedResults() {
|
|
210
|
+
const completed = [];
|
|
211
|
+
for (const result of this.results.values()) {
|
|
212
|
+
if (result.status === "fulfilled") {
|
|
213
|
+
completed.push(result.value);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return completed;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Number of items remaining in the queue
|
|
220
|
+
*/
|
|
221
|
+
getPendingCount() {
|
|
222
|
+
return this.queue.length;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Number of items that have been processed
|
|
226
|
+
*/
|
|
227
|
+
getProcessedCount() {
|
|
228
|
+
return this.results.size;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Check whether the queue is currently processing
|
|
232
|
+
*/
|
|
233
|
+
isRunning() {
|
|
234
|
+
return this.state === "processing" /* Processing */;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Get the current state
|
|
238
|
+
*/
|
|
239
|
+
getState() {
|
|
240
|
+
return this.state;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Get statistics
|
|
244
|
+
*/
|
|
245
|
+
getStats() {
|
|
246
|
+
return {
|
|
247
|
+
totalProcessed: this.stats.totalProcessed,
|
|
248
|
+
totalSuccess: this.stats.totalSuccess,
|
|
249
|
+
totalFailed: this.stats.totalFailed,
|
|
250
|
+
avgProcessingTime: this.stats.totalProcessed > 0 ? this.stats.totalProcessingTime / this.stats.totalProcessed : 0,
|
|
251
|
+
successRate: this.stats.totalProcessed > 0 ? this.stats.totalSuccess / this.stats.totalProcessed : 0
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
// ========== Private Methods ==========
|
|
255
|
+
/**
|
|
256
|
+
* Process the next items if there are available slots
|
|
257
|
+
*/
|
|
258
|
+
async processNext() {
|
|
259
|
+
if (this.state !== "processing" /* Processing */) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
while (this.queue.length > 0 && this.activeCount < this.options.concurrency) {
|
|
263
|
+
const queueItem = this.queue.shift();
|
|
264
|
+
if (queueItem) {
|
|
265
|
+
this.activeCount++;
|
|
266
|
+
this.processItem(queueItem).catch(() => {
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
await this.checkIdle();
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Process a single item
|
|
274
|
+
*/
|
|
275
|
+
async processItem(queueItem) {
|
|
276
|
+
const processStartTime = Date.now();
|
|
277
|
+
try {
|
|
278
|
+
if (this.options.yieldLoop) {
|
|
279
|
+
await yieldToEventLoop();
|
|
280
|
+
}
|
|
281
|
+
const result = await this.executeTask(queueItem.item, queueItem.index);
|
|
282
|
+
this.handleSuccess({ queueItem, result, processStartTime });
|
|
283
|
+
} catch (error) {
|
|
284
|
+
await this.handleError({ queueItem, error, processStartTime });
|
|
285
|
+
}
|
|
286
|
+
await this.finalizeTask();
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Execute the task with optional timeout
|
|
290
|
+
*/
|
|
291
|
+
async executeTask(item, index) {
|
|
292
|
+
const taskPromise = this.onItemHandler(item, index);
|
|
293
|
+
return this.options.timeout ? await withTimeout(taskPromise, this.options.timeout, item, index) : await taskPromise;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Handle successful task execution
|
|
297
|
+
*/
|
|
298
|
+
async handleSuccess(params) {
|
|
299
|
+
const { queueItem, result, processStartTime } = params;
|
|
300
|
+
const totalTime = Date.now() - processStartTime;
|
|
301
|
+
this.recordSuccess({ index: queueItem.index, value: result, totalTime });
|
|
302
|
+
await this.options.onTaskComplete?.call(this, result, queueItem.item, queueItem.index);
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Handle task execution error
|
|
306
|
+
*/
|
|
307
|
+
async handleError(params) {
|
|
308
|
+
const { queueItem, error, processStartTime } = params;
|
|
309
|
+
const { item, index, retryCount } = queueItem;
|
|
310
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
311
|
+
const totalTime = Date.now() - processStartTime;
|
|
312
|
+
if (err instanceof TimeoutError) {
|
|
313
|
+
await this.recordFailure({ index, item, error: err, totalTime });
|
|
314
|
+
await this.options.onTaskComplete?.call(this, void 0, item, index);
|
|
315
|
+
if (this.options.throwOnError) {
|
|
316
|
+
this.handleFatalError(err);
|
|
317
|
+
}
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
if (retryCount < this.options.retry) {
|
|
321
|
+
await this.scheduleRetry(queueItem, err);
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
const finalError = this.options.retry > 0 ? new MaxRetryError(item, index, retryCount, err) : err;
|
|
325
|
+
await this.recordFailure({ index, item, error: finalError, totalTime });
|
|
326
|
+
await this.options.onError?.call(this, finalError, item, index);
|
|
327
|
+
await this.options.onTaskComplete?.call(this, void 0, item, index);
|
|
328
|
+
if (this.options.throwOnError) {
|
|
329
|
+
this.handleFatalError(finalError);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Finalize task processing
|
|
334
|
+
*/
|
|
335
|
+
async finalizeTask() {
|
|
336
|
+
if (this.options.delayAfterTask > 0) {
|
|
337
|
+
await delay(this.options.delayAfterTask);
|
|
338
|
+
}
|
|
339
|
+
this.activeCount--;
|
|
340
|
+
await this.processNext();
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Schedule a retry for a failed task
|
|
344
|
+
*/
|
|
345
|
+
async scheduleRetry(queueItem, error) {
|
|
346
|
+
const retryDelay = calculateRetryDelay(
|
|
347
|
+
this.options.retryDelay,
|
|
348
|
+
this.options.retryBackoff,
|
|
349
|
+
queueItem.retryCount + 1
|
|
350
|
+
);
|
|
351
|
+
if (retryDelay > 0) {
|
|
352
|
+
await delay(retryDelay);
|
|
353
|
+
}
|
|
354
|
+
queueItem.retryCount++;
|
|
355
|
+
this.queue.unshift(queueItem);
|
|
356
|
+
this.activeCount--;
|
|
357
|
+
this.processNext();
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Record successful result
|
|
361
|
+
*/
|
|
362
|
+
async recordSuccess(params) {
|
|
363
|
+
const { index, value, totalTime } = params;
|
|
364
|
+
this.results.set(index, { status: "fulfilled", value, index });
|
|
365
|
+
this.updateStats({ success: true, processingTime: totalTime });
|
|
366
|
+
await this.options.onProgress?.call(this);
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Record failed result
|
|
370
|
+
*/
|
|
371
|
+
async recordFailure(params) {
|
|
372
|
+
const { index, item, error, totalTime } = params;
|
|
373
|
+
this.results.set(index, { status: "rejected", reason: error, item, index });
|
|
374
|
+
this.updateStats({ success: false, processingTime: totalTime });
|
|
375
|
+
await this.options.onProgress?.call(this);
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Update statistics
|
|
379
|
+
*/
|
|
380
|
+
updateStats(params) {
|
|
381
|
+
const { success, processingTime } = params;
|
|
382
|
+
this.stats.totalProcessed++;
|
|
383
|
+
if (success) {
|
|
384
|
+
this.stats.totalSuccess++;
|
|
385
|
+
} else {
|
|
386
|
+
this.stats.totalFailed++;
|
|
387
|
+
}
|
|
388
|
+
this.stats.totalProcessingTime += processingTime;
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Handle fatal error (throwOnError=true)
|
|
392
|
+
*/
|
|
393
|
+
handleFatalError(error) {
|
|
394
|
+
this.state = "idle" /* Idle */;
|
|
395
|
+
this.activeCount = 0;
|
|
396
|
+
const rejectors = this.idleRejectors;
|
|
397
|
+
this.idleRejectors = [];
|
|
398
|
+
this.idleResolvers = [];
|
|
399
|
+
rejectors.forEach((reject) => reject(error));
|
|
400
|
+
throw error;
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Check if queue is idle
|
|
404
|
+
*/
|
|
405
|
+
async checkIdle() {
|
|
406
|
+
if (this.queue.length === 0 && this.activeCount === 0) {
|
|
407
|
+
this.state = "idle" /* Idle */;
|
|
408
|
+
this.resolveIdleWaiters();
|
|
409
|
+
await this.options.onIdle?.call(this);
|
|
410
|
+
await this.options.onIdle?.();
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Resolve all idle waiters
|
|
415
|
+
*/
|
|
416
|
+
resolveIdleWaiters() {
|
|
417
|
+
const resolvers = this.idleResolvers;
|
|
418
|
+
this.idleResolvers = [];
|
|
419
|
+
resolvers.forEach((resolve) => resolve());
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Build results array maintaining order
|
|
423
|
+
*/
|
|
424
|
+
buildResultsArray() {
|
|
425
|
+
const results = [];
|
|
426
|
+
for (let i = 0; i < this.nextIndex; i++) {
|
|
427
|
+
const result = this.results.get(i);
|
|
428
|
+
if (result?.status === "fulfilled") {
|
|
429
|
+
results[i] = result.value;
|
|
430
|
+
} else {
|
|
431
|
+
results[i] = void 0;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return results;
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
export { AsyncFlexLoop, MaxRetryError, QueueAbortError, QueueState, TimeoutError, calculateRetryDelay, delay, withTimeout, yieldToEventLoop };
|
|
439
|
+
//# sourceMappingURL=index.js.map
|
|
440
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/utils/delay.ts","../src/utils/timeout.ts","../src/enums.ts","../src/AsyncFlexLoop.ts"],"names":["QueueState"],"mappings":"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","file":"index.js","sourcesContent":["// src/errors.ts\n\nexport class TimeoutError extends Error {\n constructor(\n public readonly item: unknown,\n public readonly index: number,\n public readonly timeoutMs: number,\n ) {\n super(`Task at index ${index} timed out after ${timeoutMs}ms`);\n this.name = \"TimeoutError\";\n }\n}\n\nexport class MaxRetryError extends Error {\n constructor(\n public readonly item: unknown,\n public readonly index: number,\n public readonly retryCount: number,\n public readonly originalError: Error,\n ) {\n super(`Task at index ${index} failed after ${retryCount} retries: ${originalError.message}`);\n this.name = \"MaxRetryError\";\n }\n}\n\nexport class QueueAbortError extends Error {\n constructor(message = \"Queue was aborted\") {\n super(message);\n this.name = \"QueueAbortError\";\n }\n}\n","// src/utils/delay.ts\nexport const delay = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\nexport const yieldToEventLoop = (): Promise<void> => new Promise((resolve) => setTimeout(resolve, 0));\n\nexport const calculateRetryDelay = (baseDelay: number, backoff: number, attemptNumber: number): number => {\n return baseDelay * Math.pow(backoff, attemptNumber - 1);\n};\n","// src/utils/timeout.ts\n\nimport { TimeoutError } from \"../errors\";\n\nexport const withTimeout = <T>(promise: Promise<T>, ms: number, item: unknown, index: number): Promise<T> => {\n let timeoutId: ReturnType<typeof setTimeout>;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new TimeoutError(item, index, ms));\n }, ms);\n });\n\n return Promise.race([promise.finally(() => clearTimeout(timeoutId)), timeoutPromise]);\n};\n","export enum QueueState {\n Idle = \"idle\",\n Pending = \"pending\",\n Processing = \"processing\",\n Paused = \"paused\",\n}\n","// src/AsyncFlexLoop.ts\n\nimport type { AsyncFlexLoopOptions, QueueItem, TaskResult, QueueStats } from \"./types\";\nimport { TimeoutError, MaxRetryError } from \"./errors\";\nimport { delay, yieldToEventLoop, calculateRetryDelay } from \"./utils/delay\";\nimport { withTimeout } from \"./utils/timeout\";\nimport { QueueState } from \"./enums\";\n\n/**\n * AsyncFlexLoop - Flexible async array processing with dynamic concurrency\n *\n * @example\n * ```typescript\n * const queue = new AsyncFlexLoop(\n * [1, 2, 3],\n * async (item) => item * 2,\n * { concurrency: 2 }\n * );\n *\n * await queue.onIdle();\n * const results = await queue.getResults();\n * ```\n */\nexport class AsyncFlexLoop<InputType, ResponseType> {\n // ========== Private State ==========\n private state: QueueState = QueueState.Pending;\n private queue: QueueItem<InputType>[] = [];\n private activeCount = 0;\n private nextIndex = 0;\n\n // Results storage\n private readonly results: Map<number, TaskResult<ResponseType, InputType>> = new Map();\n\n // Promises for waiting\n private idleResolvers: Array<() => void> = [];\n private idleRejectors: Array<(error: Error) => void> = [];\n\n // Statistics\n private readonly stats = {\n totalProcessed: 0,\n totalSuccess: 0,\n totalFailed: 0,\n totalProcessingTime: 0,\n successRate: 0,\n };\n\n // ========== Handler & Options ==========\n private readonly onItemHandler: (item: InputType, index: number) => Promise<ResponseType>;\n\n private readonly options: Required<\n Omit<\n AsyncFlexLoopOptions<InputType, ResponseType>,\n \"onError\" | \"onTaskComplete\" | \"onProgress\" | \"onIdle\" | \"timeout\"\n >\n > & {\n timeout?: number;\n onError?: AsyncFlexLoopOptions<InputType, ResponseType>[\"onError\"];\n onTaskComplete?: AsyncFlexLoopOptions<InputType, ResponseType>[\"onTaskComplete\"];\n onProgress?: AsyncFlexLoopOptions<InputType, ResponseType>[\"onProgress\"];\n onIdle?: AsyncFlexLoopOptions<InputType, ResponseType>[\"onIdle\"];\n };\n\n /**\n * Constructor with required parameters\n */\n constructor(\n initialItems: InputType[],\n onItem: (item: InputType, index: number) => Promise<ResponseType>,\n options?: AsyncFlexLoopOptions<InputType, ResponseType>,\n ) {\n this.onItemHandler = onItem;\n\n // Merge with defaults\n this.options = {\n concurrency: options?.concurrency ?? Infinity,\n autoStart: options?.autoStart ?? true,\n retry: options?.retry ?? 0,\n retryDelay: options?.retryDelay ?? 0,\n retryBackoff: options?.retryBackoff ?? 1,\n delayAfterTask: options?.delayAfterTask ?? 0,\n timeout: options?.timeout,\n yieldLoop: options?.yieldLoop ?? true,\n throwOnError: options?.throwOnError ?? true,\n onError: options?.onError,\n onTaskComplete: options?.onTaskComplete,\n onProgress: options?.onProgress,\n onIdle: options?.onIdle,\n };\n\n // Add initial items\n if (initialItems.length > 0) {\n this.push(...initialItems);\n }\n\n // Auto start if enabled and has items\n if (this.options.autoStart) {\n this.start();\n }\n }\n\n // ========== Public Methods ==========\n\n /**\n * Add items to the queue\n * @returns Array of assigned indices\n */\n push(...items: InputType[]): number[] {\n const indices: number[] = [];\n\n for (const item of items) {\n const queueItem: QueueItem<InputType> = {\n item,\n index: this.nextIndex,\n retryCount: 0,\n addedAt: Date.now(),\n };\n\n this.queue.push(queueItem);\n indices.push(this.nextIndex);\n this.nextIndex++;\n }\n\n // Auto-resume if was idle (queue finished and now has new work)\n if (this.state === QueueState.Idle) {\n this.start();\n }\n // Continue processing if already running\n else if (this.state === QueueState.Processing) {\n this.processNext();\n }\n // If pending or paused, do nothing (user controls start/resume)\n\n return indices;\n }\n\n /**\n * Start processing the queue\n */\n start(): void {\n // Ignore if already processing\n if (this.state === QueueState.Processing) {\n return;\n }\n\n // If paused, just resume\n if (this.state === QueueState.Paused) {\n this.resume();\n return;\n }\n\n this.state = QueueState.Processing;\n this.processNext();\n }\n\n /**\n * Pause processing (currently running tasks will finish)\n */\n pause(): void {\n this.state = QueueState.Paused;\n }\n\n /**\n * Resume processing after a pause\n */\n resume(): void {\n if (this.state === QueueState.Paused) {\n this.state = QueueState.Processing;\n this.processNext();\n }\n }\n\n /**\n * Clear all remaining items in the queue\n */\n clear(): void {\n this.queue = [];\n }\n\n /**\n * Wait until the queue is empty and all tasks have completed\n */\n onIdle(): Promise<void> {\n if (this.state === QueueState.Idle || (this.queue.length === 0 && this.activeCount === 0)) {\n return Promise.resolve();\n }\n\n return new Promise((resolve, reject) => {\n this.idleResolvers.push(resolve);\n this.idleRejectors.push(reject);\n });\n }\n\n getNextItem(): InputType | undefined {\n return this.queue[0]?.item;\n }\n\n /**\n * Get results (waits for queue to finish)\n * @returns Array with undefined for failed tasks\n */\n async getResults(): Promise<(ResponseType | undefined)[]> {\n await this.onIdle();\n return this.buildResultsArray();\n }\n\n /**\n * Get raw results with full information (waits for queue to finish)\n */\n async getRawResults(): Promise<TaskResult<ResponseType, InputType>[]> {\n await this.onIdle();\n return Array.from(this.results.values()).sort((a, b) => a.index - b.index);\n }\n\n /**\n * Get only successful results (waits for queue to finish)\n */\n async getCompletedResultsAsync(): Promise<ResponseType[]> {\n await this.onIdle();\n return this.getCompletedResults();\n }\n\n /**\n * Get current successful results (does not wait)\n */\n getCompletedResults(): ResponseType[] {\n const completed: ResponseType[] = [];\n for (const result of this.results.values()) {\n if (result.status === \"fulfilled\") {\n completed.push(result.value);\n }\n }\n return completed;\n }\n\n /**\n * Number of items remaining in the queue\n */\n getPendingCount(): number {\n return this.queue.length;\n }\n\n /**\n * Number of items that have been processed\n */\n getProcessedCount(): number {\n return this.results.size;\n }\n\n /**\n * Check whether the queue is currently processing\n */\n isRunning(): boolean {\n return this.state === QueueState.Processing;\n }\n\n /**\n * Get the current state\n */\n getState(): QueueState {\n return this.state;\n }\n\n /**\n * Get statistics\n */\n getStats(): QueueStats {\n return {\n totalProcessed: this.stats.totalProcessed,\n totalSuccess: this.stats.totalSuccess,\n totalFailed: this.stats.totalFailed,\n avgProcessingTime: this.stats.totalProcessed > 0 ? this.stats.totalProcessingTime / this.stats.totalProcessed : 0,\n successRate: this.stats.totalProcessed > 0 ? this.stats.totalSuccess / this.stats.totalProcessed : 0,\n };\n }\n\n // ========== Private Methods ==========\n\n /**\n * Process the next items if there are available slots\n */\n private async processNext(): Promise<void> {\n if (this.state !== QueueState.Processing) {\n return;\n }\n\n while (this.queue.length > 0 && this.activeCount < this.options.concurrency) {\n const queueItem = this.queue.shift();\n if (queueItem) {\n this.activeCount++;\n this.processItem(queueItem).catch(() => {\n // Errors are already handled internally (rejecting idle waiters via handleFatalError)\n });\n }\n }\n\n await this.checkIdle();\n }\n\n /**\n * Process a single item\n */\n private async processItem(queueItem: QueueItem<InputType>): Promise<void> {\n const processStartTime = Date.now();\n\n try {\n if (this.options.yieldLoop) {\n await yieldToEventLoop();\n }\n\n const result = await this.executeTask(queueItem.item, queueItem.index);\n this.handleSuccess({ queueItem, result, processStartTime });\n } catch (error) {\n await this.handleError({ queueItem, error, processStartTime });\n }\n\n await this.finalizeTask();\n }\n\n /**\n * Execute the task with optional timeout\n */\n private async executeTask(item: InputType, index: number): Promise<ResponseType> {\n const taskPromise = this.onItemHandler(item, index);\n\n return this.options.timeout ? await withTimeout(taskPromise, this.options.timeout, item, index) : await taskPromise;\n }\n\n /**\n * Handle successful task execution\n */\n private async handleSuccess(params: {\n queueItem: QueueItem<InputType>;\n result: ResponseType;\n processStartTime: number;\n }): Promise<void> {\n const { queueItem, result, processStartTime } = params;\n const totalTime = Date.now() - processStartTime;\n\n this.recordSuccess({ index: queueItem.index, value: result, totalTime });\n await this.options.onTaskComplete?.call(this, result, queueItem.item, queueItem.index);\n }\n\n /**\n * Handle task execution error\n */\n private async handleError(params: {\n queueItem: QueueItem<InputType>;\n error: unknown;\n processStartTime: number;\n }): Promise<void> {\n const { queueItem, error, processStartTime } = params;\n const { item, index, retryCount } = queueItem;\n const err = error instanceof Error ? error : new Error(String(error));\n const totalTime = Date.now() - processStartTime;\n\n // Handle timeout separately (no retry)\n if (err instanceof TimeoutError) {\n await this.recordFailure({ index, item, error: err, totalTime });\n await this.options.onTaskComplete?.call(this, undefined, item, index);\n\n if (this.options.throwOnError) {\n this.handleFatalError(err);\n }\n return;\n }\n\n // Check retry\n if (retryCount < this.options.retry) {\n await this.scheduleRetry(queueItem, err);\n return;\n }\n\n // Max retries reached\n const finalError = this.options.retry > 0 ? new MaxRetryError(item, index, retryCount, err) : err;\n\n await this.recordFailure({ index, item, error: finalError, totalTime });\n await this.options.onError?.call(this, finalError, item, index);\n await this.options.onTaskComplete?.call(this, undefined, item, index);\n\n if (this.options.throwOnError) {\n this.handleFatalError(finalError);\n }\n }\n\n /**\n * Finalize task processing\n */\n private async finalizeTask(): Promise<void> {\n // Delay after task\n if (this.options.delayAfterTask > 0) {\n await delay(this.options.delayAfterTask);\n }\n\n // Process next (only if still processing)\n this.activeCount--;\n await this.processNext();\n }\n\n /**\n * Schedule a retry for a failed task\n */\n private async scheduleRetry(queueItem: QueueItem<InputType>, error: Error): Promise<void> {\n const retryDelay = calculateRetryDelay(\n this.options.retryDelay,\n this.options.retryBackoff,\n queueItem.retryCount + 1,\n );\n\n if (retryDelay > 0) {\n await delay(retryDelay);\n }\n\n // Re-queue with incremented retry count\n queueItem.retryCount++;\n this.queue.unshift(queueItem); // Add to front\n\n this.activeCount--;\n this.processNext();\n }\n\n /**\n * Record successful result\n */\n private async recordSuccess(params: { index: number; value: ResponseType; totalTime: number }): Promise<void> {\n const { index, value, totalTime } = params;\n\n this.results.set(index, { status: \"fulfilled\", value, index });\n this.updateStats({ success: true, processingTime: totalTime });\n await this.options.onProgress?.call(this);\n }\n\n /**\n * Record failed result\n */\n private async recordFailure(params: {\n index: number;\n item: InputType;\n error: Error;\n totalTime: number;\n }): Promise<void> {\n const { index, item, error, totalTime } = params;\n\n this.results.set(index, { status: \"rejected\", reason: error, item, index });\n this.updateStats({ success: false, processingTime: totalTime });\n await this.options.onProgress?.call(this);\n }\n\n /**\n * Update statistics\n */\n private updateStats(params: { success: boolean; processingTime: number }): void {\n const { success, processingTime } = params;\n\n this.stats.totalProcessed++;\n if (success) {\n this.stats.totalSuccess++;\n } else {\n this.stats.totalFailed++;\n }\n\n this.stats.totalProcessingTime += processingTime;\n }\n\n /**\n * Handle fatal error (throwOnError=true)\n */\n private handleFatalError(error: Error): void {\n this.state = QueueState.Idle;\n this.activeCount = 0;\n\n // Reject all idle waiters\n const rejectors = this.idleRejectors;\n this.idleRejectors = [];\n this.idleResolvers = [];\n rejectors.forEach((reject) => reject(error));\n\n throw error;\n }\n\n /**\n * Check if queue is idle\n */\n private async checkIdle(): Promise<void> {\n if (this.queue.length === 0 && this.activeCount === 0) {\n this.state = QueueState.Idle;\n this.resolveIdleWaiters();\n await this.options.onIdle?.call(this);\n\n // Fire onIdle callback\n await this.options.onIdle?.();\n }\n }\n\n /**\n * Resolve all idle waiters\n */\n private resolveIdleWaiters(): void {\n const resolvers = this.idleResolvers;\n this.idleResolvers = [];\n resolvers.forEach((resolve) => resolve());\n }\n\n /**\n * Build results array maintaining order\n */\n private buildResultsArray(): (ResponseType | undefined)[] {\n const results: (ResponseType | undefined)[] = [];\n for (let i = 0; i < this.nextIndex; i++) {\n const result = this.results.get(i);\n if (result?.status === \"fulfilled\") {\n results[i] = result.value;\n } else {\n results[i] = undefined;\n }\n }\n return results;\n }\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "async-flex-loop",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Flexible async array processing with dynamic concurrency",
|
|
5
|
+
"author": "Tan Mac Duc <tanducmac@gmail.com>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/index.cjs",
|
|
9
|
+
"module": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"async",
|
|
13
|
+
"queue",
|
|
14
|
+
"concurrency",
|
|
15
|
+
"loop",
|
|
16
|
+
"array"
|
|
17
|
+
],
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"prepublishOnly": "bun run build",
|
|
23
|
+
"build": "tsup",
|
|
24
|
+
"test": "bun test",
|
|
25
|
+
"test:watch": "bun test --watch",
|
|
26
|
+
"prettier": "prettier --check .",
|
|
27
|
+
"prettier:fix": "prettier --write .",
|
|
28
|
+
"lint-staged": "lint-staged",
|
|
29
|
+
"prepare": "husky"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/bun": "latest",
|
|
33
|
+
"husky": "^9.1.7",
|
|
34
|
+
"lint-staged": "^16.2.7",
|
|
35
|
+
"prettier": "^3.8.0",
|
|
36
|
+
"tsup": "^8.5.1",
|
|
37
|
+
"typescript": "^5.9.3"
|
|
38
|
+
}
|
|
39
|
+
}
|