Show me how the concurrency limiter actually queues tasks.
Show me how the concurrency limiter actually queues tasks.
About sindresorhus/p-limit · 2 messages · last activity 5/18/2026
Great! I found that p-limit uses an external dependency. Let me explain how the task queuing works based on the source code:
How p-limit Queues Tasks
The concurrency limiter uses a two-phase queueing mechanism to manage task execution. Here's how it works:
1. The Queue Structure [index.js:L14, L57-58]
The library uses the yocto-queue package (listed in package.json dependencies) as its internal queue. When a task is submitted via limit(fn), it enters this queue.
2. Task Enqueueing Process [index.js:L44-61]
When you call limit(function_, ...args), the enqueue function handles it:
const enqueue = (function_, resolve, reject, arguments_) => {
const queueItem = {reject};
// Queue the internal resolve function instead of the run function
// to preserve the asynchronous execution context.
new Promise(internalResolve => {
queueItem.run = internalResolve;
queue.enqueue(queueItem);
}).then(run.bind(undefined, function_, resolve, arguments_));
// Start processing immediately if we haven't reached the concurrency limit
if (activeCount < concurrency) {
resumeNext();
}
};
Key points:
- A
queueItemobject is created containing arejectfunction (for error handling) - The internal
runfunction is assigned via a Promise callback to preserve async execution context - The item is added to the queue
- If we haven't hit the concurrency limit,
resumeNext()is called immediately
3. Task Execution [index.js:L29-42]
The resumeNext function processes queued tasks:
const resumeNext = () => {
// Process the next queued function if we're under the concurrency limit
if (activeCount < concurrency && queue.size > 0) {
activeCount++;
queue.dequeue().run();
}
};
This checks if there's room to run more tasks (activeCount < concurrency) and dequeues the next item to execute it.
4. Task Completion [index.js:L23-26]
When a task finishes, the next function is called:
const next = () => {
activeCount--;
resumeNext();
};
This decrements the active count and immediately tries to process the next pending task.
Example Flow
With pLimit(1) and 3 tasks:
- Task 1 is submitted → activeCount = 0 < 1, so it runs immediately (activeCount = 1)
- Task 2 is submitted → activeCount = 1, not < 1, so it waits in queue
- Task 3 is submitted → activeCount = 1, not < 1, so it waits in queue
- Task 1 finishes → activeCount = 0, resumeNext() dequeues Task 2 (activeCount = 1)
- Task 2 finishes → activeCount = 0, resumeNext() dequeues Task 3 (activeCount = 1)
- Task 3 finishes → activeCount = 0
You can inspect pending tasks with limit.pendingCount and running tasks with limit.activeCount [index.js:L66-73].
FOLLOW_UPS:
- What happens when I call clearQueue()?
- How does dynamic concurrency adjustment work?
- How are async contexts preserved during queueing?
Want to ask your own question?
Open chat for sindresorhus/p-limit →Public Q&A. Generated by RepoPilot from the actual source of sindresorhus/p-limit. AI answers can be incomplete or stale — verify before relying on them.