Skip to content

Repository files navigation

runpool

All Contributors

Bounded-concurrency helpers for async tasks — a limiter plus an order-preserving concurrent map (fail-fast or settled), with AbortSignal. Zero dependencies.

CI npm version bundle size types license

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. pLimit to wrap individual calls, mapConcurrent / mapSettled to process a list.
  • Order preserved. Results line up with inputs no matter what finishes first.
  • Fail-fast or settled. mapConcurrent rejects on the first error (and aborts the shared signal to cancel in-flight work); mapSettled returns a PromiseSettledResult per item and never rejects on task errors.
  • Cancellable. Pass an AbortSignal; each task receives a signal to forward to fetch.
  • Zero dependencies, ESM + CJS + types.

Install

npm install runpool
# or: pnpm add runpool  /  yarn add runpool  /  bun add runpool

API

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 tasks

mapConcurrent(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:

Tung Tran
Tung Tran

💻 🚧

License

MIT © Tung Tran

About

Bounded-concurrency helpers for async tasks: a limiter plus order-preserving concurrent map (fail-fast or settled), with AbortSignal. Zero dependencies.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages