Run multiple promise-returning & async functions with limited concurrency
npx @tessl/cli install tessl/npm-p-limit@7.1.0p-limit provides concurrency control for JavaScript promises and async functions, enabling developers to limit the number of simultaneously executing asynchronous operations. It offers a simple API that wraps promise-returning functions with concurrency limits, preventing system overload and resource exhaustion when processing large batches of asynchronous tasks.
npm install p-limitimport pLimit from 'p-limit';For named import:
import { limitFunction } from 'p-limit';Combined import:
import pLimit, { limitFunction } from 'p-limit';For CommonJS:
const pLimit = require('p-limit');
const { limitFunction } = require('p-limit');import pLimit from 'p-limit';
const limit = pLimit(2);
const input = [
limit(() => fetchSomething('foo')),
limit(() => fetchSomething('bar')),
limit(() => doSomething()),
limit(() => doAnotherThing())
];
// Only two promises run at once
const result = await Promise.all(input);
console.log(result);p-limit is built around a simple concurrency control pattern:
pLimit() factory creates a limit function that manages concurrencyyocto-queue for efficient FIFO promise queuingCreate a limit function that restricts the number of simultaneously executing promises.
/**
* Run multiple promise-returning & async functions with limited concurrency
* @param concurrency - Concurrency limit. Minimum: 1
* @returns A limit function
*/
function pLimit(concurrency: number): LimitFunction;Usage Example:
import pLimit from 'p-limit';
const limit = pLimit(3);
// Execute with concurrency limit
const promises = [
limit(() => fetch('/api/user/1')),
limit(() => fetch('/api/user/2')),
limit(() => fetch('/api/user/3')),
limit(() => fetch('/api/user/4')), // Will wait for one of above to complete
];
const results = await Promise.all(promises);Execute promise-returning or async functions with the concurrency limit.
/**
* Execute a function with concurrency control
* @param fn - Promise-returning/async function
* @param arguments - Any arguments to pass through to fn
* @returns The promise returned by calling fn(...arguments)
*/
<Arguments extends unknown[], ReturnType>(
function_: (...arguments_: Arguments) => PromiseLike<ReturnType> | ReturnType,
...arguments_: Arguments
): Promise<ReturnType>;Usage Example:
const limit = pLimit(2);
// Pass arguments to the limited function
const result1 = await limit(processData, dataSet1, options);
const result2 = await limit(processData, dataSet2, options);Monitor the number of currently running promises.
/**
* The number of promises that are currently running
*/
readonly activeCount: number;Usage Example:
const limit = pLimit(3);
limit(() => delay(1000));
limit(() => delay(1000));
console.log(limit.activeCount); // 2Monitor the number of promises waiting to run.
/**
* The number of promises that are waiting to run (i.e. their internal fn was not called yet)
*/
readonly pendingCount: number;Usage Example:
const limit = pLimit(2);
// Queue 5 promises, 2 will run immediately, 3 will be pending
Array.from({length: 5}, () => limit(() => delay(1000)));
console.log(limit.activeCount); // 2
console.log(limit.pendingCount); // 3Get or set the concurrency limit at runtime.
/**
* Get or set the concurrency limit
*/
concurrency: number;Usage Example:
const limit = pLimit(2);
console.log(limit.concurrency); // 2
// Increase concurrency at runtime
limit.concurrency = 5;
// This will immediately start more pending promises if availableClear pending promises that are waiting to run.
/**
* Discard pending promises that are waiting to run.
* Note: This does not cancel promises that are already running.
*/
clearQueue(): void;Usage Example:
const limit = pLimit(1);
// Queue multiple operations
limit(() => delay(1000));
limit(() => delay(1000));
limit(() => delay(1000));
console.log(limit.pendingCount); // 2
limit.clearQueue();
console.log(limit.pendingCount); // 0Process an array of inputs with limited concurrency using a mapper function.
/**
* Process an array of inputs with limited concurrency
* @param array - An array containing arguments for the mapper function
* @param mapperFunction - Promise-returning/async function that processes each item
* @returns A Promise that returns an array of results
*/
map<Input, ReturnType>(
array: readonly Input[],
mapperFunction: (input: Input, index: number) => PromiseLike<ReturnType> | ReturnType
): Promise<ReturnType[]>;Usage Example:
const limit = pLimit(3);
const results = await limit.map([1, 2, 3, 4, 5], async (number, index) => {
await delay(100);
return number * 2;
});
console.log(results); // [2, 4, 6, 8, 10]Create a function with built-in concurrency limiting for repeated use.
/**
* Returns a function with limited concurrency
* @param function_ - Promise-returning/async function
* @param options - Configuration object with concurrency property
* @returns Function with limited concurrency
*/
function limitFunction<Arguments extends unknown[], ReturnType>(
function_: (...arguments_: Arguments) => PromiseLike<ReturnType>,
options: Options
): (...arguments_: Arguments) => Promise<ReturnType>;Usage Example:
import { limitFunction } from 'p-limit';
const limitedFetch = limitFunction(async (url) => {
const response = await fetch(url);
return response.json();
}, { concurrency: 2 });
// These calls will be limited to 2 concurrent executions
const results = await Promise.all([
limitedFetch('/api/data/1'),
limitedFetch('/api/data/2'),
limitedFetch('/api/data/3'),
limitedFetch('/api/data/4'),
]);interface LimitFunction {
readonly activeCount: number;
readonly pendingCount: number;
concurrency: number;
clearQueue(): void;
map<Input, ReturnType>(
array: readonly Input[],
mapperFunction: (input: Input, index: number) => PromiseLike<ReturnType> | ReturnType
): Promise<ReturnType[]>;
<Arguments extends unknown[], ReturnType>(
function_: (...arguments_: Arguments) => PromiseLike<ReturnType> | ReturnType,
...arguments_: Arguments
): Promise<ReturnType>;
}
interface Options {
readonly concurrency: number;
}p-limit validates the concurrency parameter and throws meaningful errors:
// Throws TypeError: Expected `concurrency` to be a number from 1 and up
pLimit(0); // Invalid: must be >= 1
pLimit(-1); // Invalid: must be positive
pLimit(1.5); // Invalid: must be integer
pLimit(null); // Invalid: must be numberp-limit preserves promise rejections from the original functions:
const limit = pLimit(1);
try {
await limit(() => {
throw new Error('Function failed');
});
} catch (error) {
console.log(error.message); // 'Function failed'
}