p-queue: TypeScript Concurrency Control Done Right

Coffee count: 3 - perfectly caffeinated for some TypeScript deep-diving

Today I’m analyzing p-queue, one of those libraries that makes you appreciate when someone solves a hard problem really well. This is Sindre Sorhus’s take on promise queue management with concurrency control, and after digging through its 4,755 lines across 14 files, I can see why it’s become a go-to solution.

What Makes p-queue Special

The README is refreshingly honest - this project is “feature complete” and they’re not planning major new development. Sometimes that’s exactly what you want: a stable, well-designed tool that does one thing exceptionally well.

The core use case is rate-limiting async operations. Think REST API calls where you don’t want to hammer the server, or CPU-intensive tasks that need throttling. The compiler is my copilot here, and TypeScript’s type system really shines in this implementation.

The TypeScript Architecture

Looking at the main class declaration from source/index.ts, you can see some serious generic wizardry:

export default class PQueue<QueueType extends Queue<RunFunction, EnqueueOptionsType> = PriorityQueue, EnqueueOptionsType extends QueueAddOptions = QueueAddOptions> extends EventEmitter<EventName> { // eslint-disable-line @typescript-eslint/naming-convention
	readonly #carryoverConcurrencyCount: boolean;

	readonly #isIntervalIgnored: boolean;

	#intervalCount = 0;

	readonly #intervalCap: number;

This is TypeScript flexing its muscles. The class uses complex generics to provide type safety while maintaining flexibility. You can plug in different queue implementations and option types, but everything stays strongly typed. The private fields (those # symbols) show they’re using modern JavaScript private class fields - no more _private conventions.

What caught my attention is the event-driven architecture. Extending EventEmitter means this queue can notify listeners about state changes, completions, and errors. That’s crucial for building robust async systems where you need observability.

Dependencies That Make Sense

With 16 dependencies, p-queue is surprisingly lean for what it accomplishes. The key ones tell a story:

  • eventemitter3 for the event system (lighter than Node’s built-in EventEmitter)
  • p-timeout for timeout handling (another Sindre library)
  • p-defer for promise management

The development dependencies include benchmarking tools, which suggests performance is actively measured. That’s the kind of attention to detail I love seeing in production libraries.

The Concurrency Control Magic

From what I could analyze, the library tracks interval-based concurrency (#intervalCount, #intervalCap) alongside overall concurrency limits. The #carryoverConcurrencyCount and #isIntervalIgnored fields suggest sophisticated logic for handling edge cases in concurrent execution.

Unfortunately, I couldn’t dive deeper into the actual queuing algorithms without seeing more of the implementation, but the structure suggests they’ve thought through the hard problems: What happens when tasks take longer than expected? How do you handle backpressure? How do you provide meaningful progress updates?

When to Reach for p-queue

This isn’t a Redis-backed job queue - the README explicitly points you elsewhere for server-side work. But for client-side applications, build tools, or anywhere you need to control async operation flow, p-queue looks like a solid choice.

The TypeScript support means you get excellent autocomplete and type checking. The event system means you can build reactive UIs that respond to queue state changes. And the “feature complete” status means you’re not dealing with a moving target.

Bottom Line

Sometimes the most impressive code isn’t the flashiest - it’s the code that solves a real problem elegantly and then stops. P-queue feels like that kind of library. The TypeScript implementation is sophisticated without being overcomplicated, and the architecture suggests careful consideration of real-world usage patterns.

If you’re building anything that needs controlled async execution, this is definitely worth a closer look.


Source: p-queue on GitHub