p-queue

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

Library
npm
v9.3.3
4,283 stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum. How we score it →
61 /100 Good
Development Activity 36
Maintenance 48
Community 60
Maturity 60
Momentum 40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation. How we score it →
81 /100 Excellent
Architecture 88
Code Quality 92
Innovation 62
Learning Curve 80

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

TypeScript
96%
MIT

likec4

Developer Tools · Devops

5,838

Define your software architecture as code and get always up-to-date, interactive C4 diagrams generated automatically from a DSL.

View details
89
Repo Health
83
Technical
77
Dependency
Built with
TypeScript 96%
Updated 2 weeks ago
TypeScript
97%
MPL 2.0

Logto

Authentication

14,663

Open-source auth infrastructure for SaaS and AI apps with OIDC, SAML, and RBAC

View details
92
Repo Health
83
Technical
63
Dependency
Built with
TypeScript 97%
Updated 1 weeks ago
TypeScript
52%
Other

Mattermost

Collaboration · Devops · Team Chat

39,306

Open core, self-hosted team collaboration with chat, AI agents, voice calling, and deep DevOps integrations — all under your control.

View details
96
Repo Health
87
Technical
65
Dependency
Built with
TypeScript 52%
Go 40%
Updated 2 weeks ago
TypeScript
97%
Other

nango

Authentication · Automation · Developer Tools

12,585

Build product integrations with AI using 800+ APIs — auth, proxy, and TypeScript functions on production-grade infrastructure.

View details
94
Repo Health
85
Technical
67
Dependency
Built with
TypeScript 97%
Updated 1 weeks ago
TypeScript
99%
Other

NocoBase

Low Code Platforms · No Code Platforms

24,512

Open-source AI + no-code platform that lets coding agents and people collaborate to build business systems fast on proven infrastructure.

View details
94
Repo Health
81
Technical
63
Dependency
Built with
TypeScript 99%
Updated 1 weeks ago
TypeScript
64%
Other

NocoDB

Databases · Low Code Platforms · No Code Platforms

65,233

Turn any SQL database into a collaborative no-code spreadsheet with automatic REST APIs and real-time views.

View details
91
Repo Health
77
Technical
62
Dependency
Built with
TypeScript 64%
Vue 31%
Updated 1 weeks ago
TypeScript
86%
GPL 3.0

Notesnook

File Storage · Note Taking · Security

14,734

End-to-end encrypted, open-source note-taking where your data stays yours — even from the server.

View details
91
Repo Health
84
Technical
60
Dependency
Built with
TypeScript 86%
JavaScript 12%
Updated 2 weeks ago
TypeScript
98%
Other

Novu

Developer Tools

40,131

Open-source communication infrastructure that connects your products and AI agents to every channel your users live on — Inbox, Email, SMS, Push, Chat, and more.

View details
93
Repo Health
80
Technical
64
Dependency
Built with
TypeScript 98%
Updated 1 weeks ago
JavaScript
64%
AGPL 3.0

overleaf

Collaboration · Productivity

18,220

Open-source, real-time collaborative LaTeX editor with sandboxed compilation and full TeXLive support for self-hosted academic and research teams.

View details
86
Repo Health
80
Technical
59
Dependency
Built with
JavaScript 64%
TypeScript 30%
Updated 1 months 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