runpool
v0.1.1
Published
Bounded-concurrency helpers for async tasks: a limiter plus order-preserving concurrent map (fail-fast or settled), with AbortSignal. Zero dependencies.
Maintainers
Readme
runpool
Bounded-concurrency helpers for async tasks — a limiter plus an order-preserving concurrent map (fail-fast or settled), with
AbortSignal. Zero dependencies.
await Promise.all(items.map(fetchThing)) fires every request at once — great
until you melt the target API with 5,000 simultaneous connections. runpool caps
how many run concurrently, keeps results in input order, and lets you cancel the
whole batch with an AbortSignal.
import { mapConcurrent } from "runpool";
const pages = await mapConcurrent(
urls,
(url, { signal }) => fetch(url, { signal }).then((r) => r.text()),
{ concurrency: 8 },
);
// at most 8 requests in flight; `pages` is in the same order as `urls`Why runpool?
- Two clean primitives.
pLimitto wrap individual calls,mapConcurrent/mapSettledto process a list. - Order preserved. Results line up with inputs no matter what finishes first.
- Fail-fast or settled.
mapConcurrentrejects on the first error (and aborts the shared signal to cancel in-flight work);mapSettledreturns aPromiseSettledResultper item and never rejects on task errors. - Cancellable. Pass an
AbortSignal; each task receives a signal to forward tofetch. - Zero dependencies, ESM + CJS + types.
Install
npm install runpool
# or: pnpm add runpool / yarn add runpool / bun add runpoolAPI
pLimit(concurrency) → limiter
Wrap individual async calls so at most concurrency run at once.
import { pLimit } from "runpool";
const limit = pLimit(5);
const results = await Promise.all(jobs.map((job) => limit(() => run(job))));
limit.activeCount; // running now
limit.pendingCount; // waiting for a slot
limit.clearQueue(); // drop queued tasksmapConcurrent(items, mapper, options?) → Promise<R[]>
Map with a concurrency cap, results in input order. Fail-fast: the first rejection rejects the call and aborts the shared signal.
const out = await mapConcurrent(items, (item, { index, signal }) => work(item, signal), {
concurrency: 10,
signal: ac.signal,
});mapSettled(items, mapper, options?) → Promise<PromiseSettledResult<R>[]>
Same pool, but collects every outcome instead of failing fast:
const settled = await mapSettled(items, work, { concurrency: 10 });
const ok = settled.filter((r) => r.status === "fulfilled");| Option | Type | Default | Description |
| ------------- | -------------- | ---------- | ---------------------------------------- |
| concurrency | number | Infinity | Max tasks running at once. |
| signal | AbortSignal | — | Abort the whole run. |
The mapper receives (item, { index, signal }).
Pairs well with
retryfn— retry each task inside the pool.flightcache— dedupe/cache the work the pool runs.
Contributors ✨
This project follows the all-contributors specification. Contributions of any kind are welcome — code, docs, bug reports, ideas, reviews! See the emoji key for how each contribution is recognized, and open a PR or issue to get involved.
Thanks goes to these wonderful people:
License
MIT © Tung Tran
