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