@mystogab/promise-pool 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 +87 -0
- package/dist/index.d.mts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# @mystogab/promise-pool 🚀
|
|
2
|
+
|
|
3
|
+
A lightweight, high-performance, and memory-efficient asynchronous pool for JavaScript and TypeScript. Designed for **modern Node.js** environments (20+).
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com)
|
|
6
|
+
[](https://opensource.org)
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
## Why Promise Pool?
|
|
10
|
+
|
|
11
|
+
Unlike other libraries that use "batching" (waiting for the slowest task in a group to finish), **@mystogab/promise-pool** uses a **Dynamic Worker Queue**. As soon as a task finishes, a worker picks up the next one immediately.
|
|
12
|
+
|
|
13
|
+
- **Stream-Friendly:** Supports `Iterable` and `AsyncIterable`. Process millions of items without loading them all into memory.
|
|
14
|
+
- **Smart Control:** Stop execution gracefully using `POOL_STOP_SIGNAL`.
|
|
15
|
+
- **Dual Build:** Native support for ESM and CommonJS.
|
|
16
|
+
- **Zero Dependencies:** Ultra-light footprint for your project.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm i @mystogab/promise-pool
|
|
24
|
+
```
|
|
25
|
+
## Quick Start
|
|
26
|
+
## Basic Usage
|
|
27
|
+
```typescript
|
|
28
|
+
import { promisePool } from '@mystogab/promise-pool';
|
|
29
|
+
|
|
30
|
+
const items = [1, 2, 3, 4, 5];
|
|
31
|
+
const task = async (id) => {
|
|
32
|
+
await new Promise(r => setTimeout(r, 100));
|
|
33
|
+
return `Result ${id}`;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const { results } = await promisePool(items, task, 2);
|
|
37
|
+
console.log(results);
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Advanced Error Handling & Early Stop
|
|
41
|
+
You can stop the entire pool if a critical error occurs (e.g., Auth Token expired) using the `POOL_STOP_SIGNAL`.
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { promisePool, POOL_STOP_SIGNAL } from '@mystogab/promise-pool';
|
|
45
|
+
|
|
46
|
+
const { results, stoppedPrematurely } = await promisePool(
|
|
47
|
+
hugeDataset,
|
|
48
|
+
processData,
|
|
49
|
+
5, // Concurrency
|
|
50
|
+
async (error, item) => {
|
|
51
|
+
if (error.status === 401) {
|
|
52
|
+
console.error("Critical error! Stopping pool...");
|
|
53
|
+
return POOL_STOP_SIGNAL;
|
|
54
|
+
}
|
|
55
|
+
console.warn(`Failed item ${item.id}: ${error.message}`);
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
```
|
|
59
|
+
## Performance Comparison
|
|
60
|
+
|
|
61
|
+
| Feature | Standard Promise.all | Common "Batch" Pools | @mystogab/promise-pool |
|
|
62
|
+
|---------|----------------------|----------------------|--------------------|
|
|
63
|
+
| Memory Usage | High (loads everything) | Medium | Ultra Low (Worker-based) |
|
|
64
|
+
| Idle Time | None | High (waits for slowest) | Zero (Continuous flow) |
|
|
65
|
+
| Async Iterators | No | Limited | Native Support |
|
|
66
|
+
| Stop Signal | No | Manual/Complex | Elegant Symbol Signal |
|
|
67
|
+
|
|
68
|
+
## API Reference
|
|
69
|
+
`promisePool<T, R>(input, iteratorFn, concurrency?, errorHandler?)`
|
|
70
|
+
|
|
71
|
+
Parameters:
|
|
72
|
+
- `input`: `Iterable<T> | AsyncIterable<T>` - The data to process.
|
|
73
|
+
- `iteratorFn`: `(item: T) => Promise<R>` - The async function to run for each item.
|
|
74
|
+
- `concurrency`: `number` (Default: `2`) - Max number of simultaneous tasks.
|
|
75
|
+
- `errorHandler`: `(error: any, item: T) => void | Promise<void> | typeof POOL_STOP_SIGNAL` - Optional handler for custom logic on failure.
|
|
76
|
+
|
|
77
|
+
## Benchmarking
|
|
78
|
+
You can measure the performance in your own environment:
|
|
79
|
+
|
|
80
|
+
```javascript
|
|
81
|
+
console.time('Pool Speed');
|
|
82
|
+
await promisePool(data, tasks, 10);
|
|
83
|
+
console.timeEnd('Pool Speed');
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## License
|
|
87
|
+
MIT © [@Mystogab]
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
declare const POOL_STOP_SIGNAL: unique symbol;
|
|
2
|
+
type ErrorHandler<T, E = any> = (error: E, item: T) => void | Promise<void> | typeof POOL_STOP_SIGNAL;
|
|
3
|
+
declare const promisePool: <T, R>(input: Iterable<T> | AsyncIterable<T>, iteratorFn: (input: T) => Promise<R>, concurrency?: number, errorHandler?: ErrorHandler<T>) => Promise<{
|
|
4
|
+
results: R[];
|
|
5
|
+
errors: Error[];
|
|
6
|
+
failedItems: T[];
|
|
7
|
+
stoppedPrematurely: boolean;
|
|
8
|
+
}>;
|
|
9
|
+
|
|
10
|
+
export { POOL_STOP_SIGNAL, promisePool };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
declare const POOL_STOP_SIGNAL: unique symbol;
|
|
2
|
+
type ErrorHandler<T, E = any> = (error: E, item: T) => void | Promise<void> | typeof POOL_STOP_SIGNAL;
|
|
3
|
+
declare const promisePool: <T, R>(input: Iterable<T> | AsyncIterable<T>, iteratorFn: (input: T) => Promise<R>, concurrency?: number, errorHandler?: ErrorHandler<T>) => Promise<{
|
|
4
|
+
results: R[];
|
|
5
|
+
errors: Error[];
|
|
6
|
+
failedItems: T[];
|
|
7
|
+
stoppedPrematurely: boolean;
|
|
8
|
+
}>;
|
|
9
|
+
|
|
10
|
+
export { POOL_STOP_SIGNAL, promisePool };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use strict';var P=Symbol("POOL_STOP_SIGNAL"),p=async(t,i,l=2,s)=>{let a=[],n=[],c=[],r=false,y=t[Symbol.asyncIterator]?t[Symbol.asyncIterator]():t[Symbol.iterator](),m=Array.from({length:l},async()=>{for(;!r;){let{value:T,done:b}=await y.next();if(b||r)break;let o=T;try{let e=await i(o);r||a.push(e);}catch(e){c.push(e),n.push(o),s&&await s(e,o)===P&&(r=true);}}});return await Promise.all(m),{results:a,errors:c,failedItems:n,stoppedPrematurely:r}};exports.POOL_STOP_SIGNAL=P;exports.promisePool=p;//# sourceMappingURL=index.js.map
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["POOL_STOP_SIGNAL","promisePool","input","iteratorFn","concurrency","errorHandler","results","failedItems","errors","isAborted","iterable","workers","value","done","item","res","err"],"mappings":"aAAO,IAAMA,CAAAA,CAAmB,MAAA,CAAO,kBAAkB,CAAA,CAI5CC,CAAAA,CAAc,MACzBC,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CAAc,CAAA,CACdC,CAAAA,GACG,CACH,IAAMC,CAAAA,CAAe,EAAC,CAChBC,CAAAA,CAAmB,EAAC,CACpBC,CAAAA,CAAkB,EAAC,CACrBC,CAAAA,CAAY,KAAA,CAGVC,CAAAA,CAAYR,CAAAA,CAAc,MAAA,CAAO,aAAa,CAAA,CAC/CA,EAA2B,MAAA,CAAO,aAAa,CAAA,EAAE,CACjDA,CAAAA,CAAsB,MAAA,CAAO,QAAQ,CAAA,EAAE,CAwBtCS,CAAAA,CAAU,KAAA,CAAM,IAAA,CAAK,CAAE,MAAA,CAAQP,CAAY,CAAA,CAtBlC,SAAY,CACzB,KAAO,CAACK,CAAAA,EAAW,CACjB,GAAM,CAAE,KAAA,CAAAG,CAAAA,CAAO,IAAA,CAAAC,CAAK,CAAA,CAAI,MAAMH,EAAS,IAAA,EAAK,CAE5C,GAAIG,CAAAA,EAAQJ,CAAAA,CAAW,MACvB,IAAMK,CAAAA,CAAOF,CAAAA,CAEb,GAAI,CACF,IAAMG,CAAAA,CAAM,MAAMZ,CAAAA,CAAWW,CAAI,CAAA,CAC5BL,CAAAA,EAAWH,CAAAA,CAAQ,IAAA,CAAKS,CAAG,EAClC,CAAA,MAASC,CAAAA,CAAK,CACZR,CAAAA,CAAO,IAAA,CAAKQ,CAAY,CAAA,CACxBT,CAAAA,CAAY,IAAA,CAAKO,CAAI,CAAA,CACjBT,CAAAA,EACa,MAAMA,CAAAA,CAAaW,CAAAA,CAAKF,CAAI,CAAA,GAC5Bd,CAAAA,GAAkBS,CAAAA,CAAY,IAAA,EAEjD,CACF,CACF,CAG0D,CAAA,CAC1D,OAAA,MAAM,OAAA,CAAQ,GAAA,CAAIE,CAAO,CAAA,CAElB,CAAE,OAAA,CAAAL,CAAAA,CAAS,MAAA,CAAAE,CAAAA,CAAQ,WAAA,CAAAD,CAAAA,CAAa,kBAAA,CAAoBE,CAAU,CACvE","file":"index.js","sourcesContent":["export const POOL_STOP_SIGNAL = Symbol('POOL_STOP_SIGNAL');\n\ntype ErrorHandler<T, E = any> = (error: E, item: T) => void | Promise<void> | typeof POOL_STOP_SIGNAL;\n\nexport const promisePool = async <T, R>(\n input: Iterable<T> | AsyncIterable<T>,\n iteratorFn: (input: T) => Promise<R>,\n concurrency = 2,\n errorHandler?: ErrorHandler<T>\n) => {\n const results: R[] = [];\n const failedItems: T[] = [];\n const errors: Error[] = [];\n let isAborted = false;\n\n // Convert to an iterator (sync or async)\n const iterable = (input as any)[Symbol.asyncIterator] \n ? (input as AsyncIterable<T>)[Symbol.asyncIterator]()\n : (input as Iterable<T>)[Symbol.iterator]();\n\n const worker = async () => {\n while (!isAborted) {\n const { value, done } = await iterable.next();\n \n if (done || isAborted) break;\n const item = value;\n\n try {\n const res = await iteratorFn(item);\n if (!isAborted) results.push(res);\n } catch (err) {\n errors.push(err as Error);\n failedItems.push(item);\n if (errorHandler) {\n const action = await errorHandler(err, item);\n if (action === POOL_STOP_SIGNAL) isAborted = true;\n }\n }\n }\n };\n\n // Lunch workers up to the concurrency limit\n const workers = Array.from({ length: concurrency }, worker);\n await Promise.all(workers);\n\n return { results, errors, failedItems, stoppedPrematurely: isAborted };\n};\n"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var P=Symbol("POOL_STOP_SIGNAL"),p=async(t,i,l=2,s)=>{let a=[],n=[],c=[],r=false,y=t[Symbol.asyncIterator]?t[Symbol.asyncIterator]():t[Symbol.iterator](),m=Array.from({length:l},async()=>{for(;!r;){let{value:T,done:b}=await y.next();if(b||r)break;let o=T;try{let e=await i(o);r||a.push(e);}catch(e){c.push(e),n.push(o),s&&await s(e,o)===P&&(r=true);}}});return await Promise.all(m),{results:a,errors:c,failedItems:n,stoppedPrematurely:r}};export{P as POOL_STOP_SIGNAL,p as promisePool};//# sourceMappingURL=index.mjs.map
|
|
2
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["POOL_STOP_SIGNAL","promisePool","input","iteratorFn","concurrency","errorHandler","results","failedItems","errors","isAborted","iterable","workers","value","done","item","res","err"],"mappings":"AAAO,IAAMA,CAAAA,CAAmB,MAAA,CAAO,kBAAkB,CAAA,CAI5CC,CAAAA,CAAc,MACzBC,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CAAc,CAAA,CACdC,CAAAA,GACG,CACH,IAAMC,CAAAA,CAAe,EAAC,CAChBC,CAAAA,CAAmB,EAAC,CACpBC,CAAAA,CAAkB,EAAC,CACrBC,CAAAA,CAAY,KAAA,CAGVC,CAAAA,CAAYR,CAAAA,CAAc,MAAA,CAAO,aAAa,CAAA,CAC/CA,EAA2B,MAAA,CAAO,aAAa,CAAA,EAAE,CACjDA,CAAAA,CAAsB,MAAA,CAAO,QAAQ,CAAA,EAAE,CAwBtCS,CAAAA,CAAU,KAAA,CAAM,IAAA,CAAK,CAAE,MAAA,CAAQP,CAAY,CAAA,CAtBlC,SAAY,CACzB,KAAO,CAACK,CAAAA,EAAW,CACjB,GAAM,CAAE,KAAA,CAAAG,CAAAA,CAAO,IAAA,CAAAC,CAAK,CAAA,CAAI,MAAMH,EAAS,IAAA,EAAK,CAE5C,GAAIG,CAAAA,EAAQJ,CAAAA,CAAW,MACvB,IAAMK,CAAAA,CAAOF,CAAAA,CAEb,GAAI,CACF,IAAMG,CAAAA,CAAM,MAAMZ,CAAAA,CAAWW,CAAI,CAAA,CAC5BL,CAAAA,EAAWH,CAAAA,CAAQ,IAAA,CAAKS,CAAG,EAClC,CAAA,MAASC,CAAAA,CAAK,CACZR,CAAAA,CAAO,IAAA,CAAKQ,CAAY,CAAA,CACxBT,CAAAA,CAAY,IAAA,CAAKO,CAAI,CAAA,CACjBT,CAAAA,EACa,MAAMA,CAAAA,CAAaW,CAAAA,CAAKF,CAAI,CAAA,GAC5Bd,CAAAA,GAAkBS,CAAAA,CAAY,IAAA,EAEjD,CACF,CACF,CAG0D,CAAA,CAC1D,OAAA,MAAM,OAAA,CAAQ,GAAA,CAAIE,CAAO,CAAA,CAElB,CAAE,OAAA,CAAAL,CAAAA,CAAS,MAAA,CAAAE,CAAAA,CAAQ,WAAA,CAAAD,CAAAA,CAAa,kBAAA,CAAoBE,CAAU,CACvE","file":"index.mjs","sourcesContent":["export const POOL_STOP_SIGNAL = Symbol('POOL_STOP_SIGNAL');\n\ntype ErrorHandler<T, E = any> = (error: E, item: T) => void | Promise<void> | typeof POOL_STOP_SIGNAL;\n\nexport const promisePool = async <T, R>(\n input: Iterable<T> | AsyncIterable<T>,\n iteratorFn: (input: T) => Promise<R>,\n concurrency = 2,\n errorHandler?: ErrorHandler<T>\n) => {\n const results: R[] = [];\n const failedItems: T[] = [];\n const errors: Error[] = [];\n let isAborted = false;\n\n // Convert to an iterator (sync or async)\n const iterable = (input as any)[Symbol.asyncIterator] \n ? (input as AsyncIterable<T>)[Symbol.asyncIterator]()\n : (input as Iterable<T>)[Symbol.iterator]();\n\n const worker = async () => {\n while (!isAborted) {\n const { value, done } = await iterable.next();\n \n if (done || isAborted) break;\n const item = value;\n\n try {\n const res = await iteratorFn(item);\n if (!isAborted) results.push(res);\n } catch (err) {\n errors.push(err as Error);\n failedItems.push(item);\n if (errorHandler) {\n const action = await errorHandler(err, item);\n if (action === POOL_STOP_SIGNAL) isAborted = true;\n }\n }\n }\n };\n\n // Lunch workers up to the concurrency limit\n const workers = Array.from({ length: concurrency }, worker);\n await Promise.all(workers);\n\n return { results, errors, failedItems, stoppedPrematurely: isAborted };\n};\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mystogab/promise-pool",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"main": "./dist/index.js",
|
|
5
|
+
"module": "./dist/index.mjs",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"require": "./dist/index.js",
|
|
10
|
+
"import": "./dist/index.mjs",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsup",
|
|
20
|
+
"dev": "tsup --watch",
|
|
21
|
+
"test": "node --test src/index.test.ts",
|
|
22
|
+
"lint": "tsc",
|
|
23
|
+
"prepublishOnly": "npm run test && npm run build"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^25.1.0",
|
|
27
|
+
"tsup": "^8.5.1",
|
|
28
|
+
"typescript": "^5.9.3"
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=20.0.0",
|
|
32
|
+
"npm": ">=10.0.0"
|
|
33
|
+
},
|
|
34
|
+
"author": {
|
|
35
|
+
"name": "Gabriel Desimone",
|
|
36
|
+
"email": "gabriel.desimone@mail.com",
|
|
37
|
+
"url": "https://github.com/Mystogab"
|
|
38
|
+
},
|
|
39
|
+
"license": "MIT"
|
|
40
|
+
}
|