p-queue

Promise queue with concurrency control, interval-based rate limiting, and priority scheduling for async tasks.

Library
npm
v9.3.3
4,265stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
66/100Good
Development Activity48
Maintenance56
Community60
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
81/100Excellent
Architecture88
Code Quality92
Innovation62
Learning Curve80

p-queue is a promise queue for Node.js and modern browsers that runs async (or sync) functions with a configurable concurrency limit. Instead of firing every task at once, callers .add() work to the queue and p-queue dequeues it as capacity frees up, making it a common building block for rate-limiting calls to REST APIs, throttling CPU/memory-intensive jobs, or capping parallel file operations.

Beyond a basic concurrency cap, it supports interval-based throttling (intervalCap + interval, with an optional sliding-window strict mode), per-task priority so higher-priority work jumps the queue, per-task or per-queue timeouts via p-timeout, and AbortSignal cancellation. It exposes an EventEmitter3-based event surface (active, idle, empty, next, completed, error, rateLimit, and more) plus promise-returning helpers like .onIdle(), .onEmpty(), and .onSizeLessThan() for coordinating queue state without polling.

What You Get

  • A PQueue class you instantiate with concurrency, intervalCap/interval, timeout, and autoStart options, then feed work to via .add() or .addAll()
  • A pluggable queueClass — the default PriorityQueue keeps a sorted, amortized-O(1) array so higher-priority tasks run first, and you can swap in your own class implementing enqueue/dequeue/size
  • Interval-based throttling with a fixed-window default and an opt-in strict sliding-window mode that tracks execution timestamps to prevent bursts at window boundaries
  • Promise-returning state hooks — .onEmpty(), .onIdle(), .onPendingZero(), .onRateLimit(), .onRateLimitCleared(), .onSizeLessThan(), .onError() — so you can await queue transitions instead of listening for events
  • Per-task cancellation via AbortSignal and per-task or per-queue timeout (backed by p-timeout, raising TimeoutError) without writing your own timer bookkeeping

Common Use Cases

  • Rate-limiting calls to a third-party REST API that enforces a requests-per-second cap
  • Throttling CPU- or memory-intensive tasks (image processing, file I/O) to a fixed number of parallel workers
  • Reordering in-flight work by priority — e.g. serving interactive requests ahead of background batch jobs sharing the same worker pool
  • Coordinating multiple producers adding work over time by awaiting .onIdle() or .onEmpty() instead of managing a manual counter

Under The Hood

Architecture Execution flows from a single public entry point, PQueue in source/index.ts, which owns all scheduling state (concurrency counters, interval timers, pending-task bookkeeping) and delegates ordering to an injected queue implementation via the queueClass option — source/priority-queue.ts by default. This separation of concerns means the class enforces concurrency, timeout, and rate-limit invariants directly, while pluggable, encapsulated logic (implementing the small Queue interface from source/queue.ts) decides what runs next; swapping queueClass changes ordering strategy without touching any scheduling code. Interval-based rate limiting runs as a small state machine of its own (#initializeIntervalIfNeeded, #onInterval, recurring vs one-shot timers) layered on top of the same dequeue loop the plain-concurrency path uses, so strict sliding-window mode extends rather than forks the core scheduling logic. The design is intentionally a flat orchestrator plus one swappable strategy object, not a deep layered architecture — reasonable for a focused primitive, and it means the core abstraction that would break the most if changed is the Queue interface’s contract (enqueue/dequeue/size/setPriority/remove), since every scheduling decision funnels through it.

Tech Stack Written in strict TypeScript targeting Node.js 20+, compiled with tsc against @sindresorhus/tsconfig, and shipped as ESM-only (no CommonJS export, "type": "module"). Runtime dependencies are minimal and purposeful: eventemitter3 supplies the typed event-emitter base class PQueue extends, and p-timeout implements the per-task/per-queue timeout wrapping (including its TimeoutError). There is no build framework beyond tsc plus del-cli for cleaning dist/; the package ships only compiled dist/ output per its files field. CI (.github/workflows) runs the test suite via npm test across a Node 20/24 matrix on GitHub Actions.

Code Quality Testing uses Node’s built-in node:test runner (via tsx/esm) across seven dedicated files (basic.ts, advanced.ts, strict.ts, rate-limit.ts, priority-queue.ts, validation.ts, debug.ts) totaling well over 100KB of test code, exercising concurrency edge cases, timing-sensitive rate-limit behavior, priority ordering, and input validation with real timers and assert/strict. Linting is enforced through xo (a strict, opinionated ESLint preset) with only a small, explicitly justified set of TypeScript rule overrides, and tsd runs type-level tests against the public .d.ts output in test-d/ to guard the published type signatures themselves, not just runtime behavior. Source code favors private class fields (#), explicit TypeError throws with descriptive messages for invalid options, and inline comments explaining non-obvious invariants (e.g. the circular-buffer bookkeeping in #cleanupStrictTicks) rather than leaving them undocumented.

API Design The public surface is a single constructor plus a small, consistently-named method set (.add(), .addAll(), .pause(), .start(), .clear()) paired with a family of on*() promise-returning state hooks (.onIdle(), .onEmpty(), .onPendingZero(), .onRateLimit(), .onSizeLessThan()) that let callers await queue transitions instead of wiring up event listeners by hand, while the underlying EventEmitter3 events remain available for repeated-notification cases. Getting started requires no boilerplate beyond new PQueue(options) and .add(fn); every option (concurrency, intervalCap, interval, timeout, priority) is optional with a sensible default, and JSDoc on each option in source/options.ts includes inline usage examples. The README documents every corner (custom queueClass, AbortSignal cancellation, setPriority) with runnable code samples, keeping the learning curve low despite the library’s fairly deep feature set.

Used by 22 apps in this directory

JavaScript
95%
MIT

AnythingLLM

Developer Tools · Automation · AI Assistants

65,696

The all-in-one AI platform for private document chat, no-code agents, and local LLMs with zero setup friction.

View details
90
Repo Health
78
Technical
64
Dependency
Built with
JavaScript95%
Updated 3 days ago
Rust
67%
MIT

Bun

Developer Tools

95,895

An all-in-one JavaScript and TypeScript toolkit — one Rust-and-JavaScriptCore binary that replaces Node.js, npm, a bundler, and a test runner with faster equivalents.

View details
92
Repo Health
91
Technical
64
Dependency
Built with
Rust67%
C++19%
Updated yesterday
TypeScript
99%
AGPL 3.0

Cherry Studio

AI Assistants

51,521

All-in-one AI desktop client with 300+ assistants and multi-model support

View details
89
Repo Health
84
Technical
71
Dependency
Built with
TypeScript99%
Updated today
JavaScript
90%
Other

CodeSandbox

Code Editors · Developer Tools

13,640

Instantly ready browser-based IDE that runs full npm dependency resolution and transpilation entirely client-side, with no server needed.

View details
69
Repo Health
76
Technical
62
Dependency
Built with
JavaScript90%
Updated 1 weeks ago
TypeScript
81%
Other

Directus

CMS · Low Code Platforms

37,783

Connect any SQL database and get instant REST and GraphQL APIs, a visual management Studio, and a native MCP server for AI agents — free for most organizations.

View details
92
Repo Health
82
Technical
76
Dependency
Built with
TypeScript81%
Vue18%
Updated 3 days ago
TypeScript
97%
Other

GrowthBook

Developer Tools · Analytics · Monitoring

8,299

Open source feature flags, A/B testing, and warehouse-native experimentation that queries your existing data infrastructure—no data movement required.

View details
92
Repo Health
85
Technical
63
Dependency
Built with
TypeScript97%
Updated yesterday
TypeScript
99%
MIT

GraphQL Hive

Developer Tools · Devops · Monitoring

484

Open-source GraphQL schema registry and observability platform with breaking change detection, federation support, and CI/CD integration for teams of any size.

View details
89
Repo Health
81
Technical
69
Dependency
Built with
TypeScript99%
Updated 2 days ago
TypeScript
61%
EPL-2.0

Huly Platform

Project Management · Team Chat · Collaboration

27,586

Open-source all-in-one workspace that replaces Linear, Jira, Slack, and Notion for product and engineering teams.

View details
89
Repo Health
86
Technical
62
Dependency
Built with
TypeScript61%
Svelte34%
Updated 1 weeks ago
TypeScript
57%
MIT

Jitsu

Data Engineering

5,063

Open-source, fully-scriptable data ingestion engine that streams events from web, apps, and APIs to any data warehouse in real time.

View details
88
Repo Health
79
Technical
66
Dependency
Built with
TypeScript57%
Go41%
Updated 3 days ago

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search