Recipes
February 3, 2026 ยท View on GitHub
Batch file processing
import fs from 'node:fs/promises';
import pLimit from 'p-limit';
const limit = pLimit(5);
const files = await fs.readdir('uploads');
const results = await limit.map(files, async file => {
const content = await fs.readFile(`uploads/${file}`, 'utf8');
return JSON.parse(content);
});
Fetch multiple URLs
import pLimit from 'p-limit';
const limit = pLimit(3);
const urls = [
'https://api.example.com/users/1',
'https://api.example.com/users/2',
'https://api.example.com/users/3',
'https://api.example.com/users/4',
'https://api.example.com/users/5',
];
const results = await limit.map(urls, async url => {
const response = await fetch(url);
return response.json();
});
Error handling with partial results
Use Promise.allSettled to continue processing even if some tasks fail, for example when a request returns a non-2xx response:
import pLimit from 'p-limit';
const limit = pLimit(3);
const urls = [
'https://api.example.com/users/1',
'https://api.example.com/users/2',
'https://api.example.com/users/3',
];
const results = await Promise.allSettled(
urls.map(url => limit(async () => {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Request failed with ${response.status} for ${url}`);
}
return response.json();
}))
);
for (const result of results) {
if (result.status === 'fulfilled') {
console.log(result.value);
} else {
console.error(result.reason);
}
}
Graceful shutdown
Use clearQueue() to discard pending tasks when shutting down. Promises for discarded tasks never settle, so only wait for already running tasks during shutdown:
import pLimit from 'p-limit';
const limit = pLimit(5);
const urls = getUrls(); // Assume this returns a large list of URLs
const runningPromises = new Set();
const promises = urls.map(url => limit(async () => {
const fetchPromise = fetch(url);
runningPromises.add(fetchPromise);
try {
return await fetchPromise;
} finally {
runningPromises.delete(fetchPromise);
}
}));
const shutdown = () => {
// Discard pending tasks (already running tasks will complete)
limit.clearQueue();
void Promise.allSettled([...runningPromises]).finally(() => {
process.exit(0);
});
};
for (const signal of ['SIGTERM', 'SIGINT']) {
process.once(signal, shutdown);
}
const results = await Promise.allSettled(promises);
Dynamic concurrency
Adjust concurrency at runtime, for example, in response to rate limiting:
import pLimit from 'p-limit';
const limit = pLimit(10);
const urls = getUrls(); // Assume this returns a list of URLs
async function fetchWithBackoff(url) {
const response = await fetch(url);
if (response.status === 429) {
limit.concurrency = Math.max(1, Math.floor(limit.concurrency / 2));
} else if (limit.concurrency < 10) {
limit.concurrency++;
}
return response;
}
const results = await limit.map(urls, fetchWithBackoff);
Reusable limited function
Use limitFunction() when you want a single function to manage its own concurrency:
import {limitFunction} from 'p-limit';
const urls = getUrls(); // Assume this returns a list of URLs
const fetchUrl = async url => {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Request failed with ${response.status} for ${url}`);
}
return response.json();
};
const limitedFetchUrl = limitFunction(fetchUrl, {concurrency: 3});
const results = await Promise.all(urls.map(url => limitedFetchUrl(url)));
Shared context provider
Use limit(fn, ...args) to pass a shared context (like a client instance) to all limited calls:
import pLimit from 'p-limit';
import {S3} from '@aws-sdk/client-s3';
const limit = pLimit(2);
const client = new S3({});
const runWithClient = (function_, ...arguments_) => limit(function_, client, ...arguments_);
const fetchFromSomeBucket = (client, fileKey) => client.getObject({
Bucket: 'someBucket',
Key: fileKey
});
const results = await Promise.all([
runWithClient(fetchFromSomeBucket, 'someFileKey1'),
runWithClient(fetchFromSomeBucket, 'someFileKey2'),
runWithClient(fetchFromSomeBucket, 'someFileKey3')
]);
Progress reporting
Use activeCount and pendingCount to report progress while work is running:
import pLimit from 'p-limit';
const limit = pLimit(5);
const urls = getUrls(); // Assume this returns a list of URLs
const progressInterval = setInterval(() => {
console.log(`Running: ${limit.activeCount}, pending: ${limit.pendingCount}`);
}, 250);
let results;
try {
results = await limit.map(urls, url => fetch(url));
} finally {
clearInterval(progressInterval);
}