npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@matheusbozetti/run-concurrent

v1.1.4

Published

Run async functions concurrently with a concurrency limit. A lightweight, type-safe alternative to Promise.all for batch processing and parallel task execution.

Readme

run-concurrent

Lightweight TypeScript utility to run async functions concurrently with a concurrency limit. A smarter alternative to Promise.all for batch processing, rate limiting, and parallel task execution in Node.js.

Zero dependencies | Full TypeScript support | ESM & CJS

Why not just Promise.all?

Promise.all fires every promise at once. With hundreds of tasks (API calls, DB queries, file operations), that means resource exhaustion, rate limit errors, and unpredictable behavior.

runConcurrent gives you:

  • Concurrency control — limit how many async operations run at the same time
  • Graceful error handling — continue execution even when some tasks fail (stopOnError: false)
  • Fail-fast mode — stop immediately on the first error (stopOnError: true)
  • Original error propagation — optionally rethrow the original error instead of wrapping it (throwOriginalError: true)
  • Ordered results — results come back in declaration order, not execution order
  • Strong type inference — preserves tuple types for heterogeneous task arrays

Installation

npm install @matheusbozetti/run-concurrent
# or
pnpm add @matheusbozetti/run-concurrent
# or
yarn add @matheusbozetti/run-concurrent

Quick Start

import { runConcurrent } from "@matheusbozetti/run-concurrent";

const results = await runConcurrent(
  [
    async () => fetch("/api/users").then((r) => r.json()),
    async () => fetch("/api/posts").then((r) => r.json()),
    async () => fetch("/api/comments").then((r) => r.json()),
  ],
  { concurrency: 2 }
);
// results: [users, posts, comments] — in order, 2 at a time

API

runConcurrent(tasks, options?)

Executes an array of async functions with controlled concurrency.

Parameters

| Parameter | Type | Description | | --------- | ---- | ----------- | | tasks | Array<() => Promise<T>> | Array of async functions to execute | | options | RunConcurrentOptions | Configuration (see below) |

Options

| Option | Type | Default | Description | | ------ | ---- | ------- | ----------- | | concurrency | number | 5 | Maximum number of tasks running in parallel | | stopOnError | boolean | true | If true, stops on first failure and throws. If false, continues and collects errors | | throwOriginalError | boolean | false | If true, throws/stores the original Error instead of wrapping it in ConcurrencyError |

Return Types

When stopOnError: true (default) — returns Promise<T[]>

const results = await runConcurrent(tasks, { stopOnError: true });
// T[] — array of resolved values

When stopOnError: false — returns Promise<{ data, errorIndexes }>

const results = await runConcurrent(tasks, { stopOnError: false });
// { data: (T | ConcurrencyError)[], errorIndexes: number[] }

When stopOnError: false + throwOriginalError: true — errors are the original Error instead of ConcurrencyError

const results = await runConcurrent(tasks, {
  stopOnError: false,
  throwOriginalError: true,
});
// { data: (T | Error)[], errorIndexes: number[] }

Error Handling

Default: ConcurrencyError wrapper

When a task fails, it is wrapped in a ConcurrencyError that includes:

  • message — the original error message
  • index — position of the failed task in the input array
  • originalError — reference to the original Error object
const results = await runConcurrent(
  [
    async () => "ok",
    async () => { throw new Error("timeout"); },
    async () => "ok",
  ],
  { stopOnError: false }
);

console.log(results.data);
// ["ok", ConcurrencyError { message: "timeout", index: 1 }, "ok"]
console.log(results.errorIndexes);
// [1]

Propagating the original error

Use throwOriginalError: true when you need to check instanceof or preserve custom error types:

class ApiError extends Error {
  constructor(public statusCode: number) {
    super(`HTTP ${statusCode}`);
  }
}

// With stopOnError: true — throws the original ApiError
try {
  await runConcurrent(
    [async () => { throw new ApiError(429); }],
    { stopOnError: true, throwOriginalError: true }
  );
} catch (error) {
  console.log(error instanceof ApiError); // true
  console.log(error.statusCode); // 429
}

// With stopOnError: false — stores the original error in the data array
const results = await runConcurrent(
  [async () => { throw new ApiError(429); }],
  { stopOnError: false, throwOriginalError: true }
);
console.log(results.data[0] instanceof ApiError); // true

TypeScript Type Inference

runConcurrent preserves individual return types when tasks are inlined or declared with as const:

// Inlined — TypeScript infers [number, string, boolean]
const results = await runConcurrent([
  async () => 42,
  async () => "hello",
  async () => true,
]);

// With `as const` — same inference
const tasks = [
  async () => 42,
  async () => "hello",
  async () => true,
] as const;
const results = await runConcurrent(tasks);
// [number, string, boolean]

Without as const, TypeScript widens the type to (string | number | boolean)[].

Use Cases

  • API batch requests — call multiple endpoints without overwhelming the server
  • Database bulk operations — insert/update rows with connection pool limits
  • File processing — read/write many files without exhausting file descriptors
  • Web scraping — crawl pages with controlled request rate
  • Microservice orchestration — fan-out requests to downstream services

Testing

pnpm test  # or npm test / yarn test

Tests are written with Vitest.

License

MIT License © Matheus