p-limit、p-queue、p-throttle は、JavaScript における非同期処理のフローを制御するためのユーティリティです。p-limit は同時実行数(コネクション数など)を制限し、リソースの枯渇を防ぎます。p-throttle は単位時間あたりの実行回数を制限し、API のレート制限対策などに有効です。p-queue はこれらを統合した優先度付きキューであり、タスクの順序制御やイベント監視も可能です。
JavaScript で非同期処理を扱う際、Promise.all() や単純な async/await だけでは制御しきれない場面があります。サーバーへの負荷軽減、API のレート制限遵守、リソースの競合回避 — これらの課題を解決するのが p-limit、p-queue、p-throttle です。これらは似ていますが、解決する問題が異なります。
p-limit は「同時実行数」を制限します。
import pLimit from 'p-limit';
const limit = pLimit(2); // 同時に 2 つまで
const input = [1, 2, 3, 4, 5];
const jobs = input.map(x => limit(() => fetch(`/api/${x}`)));
// すべて予約されるが、同時に実行されるのは 2 つまで
const results = await Promise.all(jobs);
p-throttle は「単位時間あたりの実行回数」を制限します。
import pThrottle from 'p-throttle';
const throttle = pThrottle({
limit: 1,
interval: 1000 // 1 秒間に 1 回
});
const throttledFetch = throttle(() => fetch('/api/data'));
// 連続して呼んでも、間隔が空けられる
await throttledFetch();
await throttledFetch(); // 1 秒待ってから実行
p-queue は「優先度付きキュー」です。
import PQueue from 'p-queue';
const queue = new PQueue({
concurrency: 2,
intervalCap: 5,
interval: 1000 // 1 秒間に 5 回まで
});
// 優先度が高いタスクを先に実行
queue.add(() => fetch('/api/high'), { priority: 10 });
queue.add(() => fetch('/api/low'), { priority: 1 });
await queue.onEmpty(); // キューが空になるのを待機
制御したい対象によって選ぶべきツールが変わります。
| 機能 | p-limit | p-throttle | p-queue |
|---|---|---|---|
| 同時実行数制限 | ✅ | ❌ | ✅ |
| 時間ベース制限 | ❌ | ✅ | ✅ |
| 優先順位 | ❌ | ❌ | ✅ |
| イベント監視 | ❌ | ❌ | ✅ |
| 一時停止/再開 | ❌ | ❌ | ✅ |
p-queue だけがタスクに優先度を付けられます。例えば、ユーザーの操作に関連するリクエストを、バックグラウンドの同期処理より先に処理したい場合に有効です。
// p-queue の場合
queue.add(criticalTask, { priority: 100 });
queue.add(backgroundTask, { priority: 1 });
// p-limit や p-throttle では順序制御ができない
p-queue は onEmpty や onIdle などのイベントを提供します。バッチ処理の完了を検知して次の処理に進むような場合に便利です。
// p-queue の場合
queue.on('empty', () => {
console.log('キューが空になりました');
});
// p-limit や p-throttle では外部で管理が必要
多数の画像を同時に読み込むとブラウザが固まります。同時実行数を制限します。
p-limitconst limit = pLimit(5);
const images = urls.map(url => limit(() => loadImg(url)));
await Promise.all(images);
無料枠の API は「1 秒間に 10 リクエスト」などの制限があります。
p-throttleconst throttle = pThrottle({ limit: 10, interval: 1000 });
const callApi = throttle(() => fetch('/external'));
await callApi();
重要なタスクと重要でないタスクが混在し、全体の実行速度も制限したい。
p-queueconst queue = new PQueue({ concurrency: 5, intervalCap: 10, interval: 1000 });
queue.add(importantJob, { priority: 10 });
queue.add(normalJob, { priority: 1 });
await queue.onIdle();
p-limit の待ち時間p-limit はキューに入れたタスクがいつ実行されるか制御できません。単に枠が空くのを待つだけです。時間制御が必要な場合は不適です。
p-throttle の遅延p-throttle は制限を超えると実行を遅延させます。即時性を求められる処理には向きません。エラーを投げたい場合は別途制御が必要です。
p-queue の複雑さ機能が多い分、設定項目も増えます。単純な同時実行数制限だけで良い場合に使うと、コードが不必要に複雑になります。
| 要件 | 推奨パッケージ |
|---|---|
| 同時に N 個だけ実行したい | p-limit |
| 1 秒間に N 回まで制限したい | p-throttle |
| 優先順位をつけたい | p-queue |
| キューが空になるのを待ちたい | p-queue |
| 両方の制限をかけたい | p-queue |
これらは競合するツールではなく、補完するツールです。
p-limit は軽量な同時実行制御 — 画像読み込みや単純なバッチ処理に。
p-throttle は時間ベースの安全装置 — API 呼び出しのレート制限対策に。
p-queue は本格的なタスク管理 — 優先度やイベント監視が必要な複雑なシステムに。
プロジェクトの要件に合わせて適切なものを選ぶことで、安定した非同期処理を実装できます。
単純に同時実行数を抑えたい場合に最適です。依存関係が少なく、バンドルサイズを小さく保ちたいプロジェクトに向いています。複雑なキュー管理が必要ない限り、最も軽量な選択肢となります。
優先順位付けや、並行性とレート制限の両方が必要な場合に選択します。タスクの完了イベントを監視したい場合にも有用です。大規模なバッチ処理や、タスクの優先度を変えたい場景で真価を発揮します。
API のレート制限など、時間ベースの制限を厳密に守る必要がある場合に使用します。間隔を空けて実行したい処理に最適です。単純な関数の呼び出し頻度を制御したい場合に適しています。
Run multiple promise-returning & async functions with limited concurrency
Works in Node.js and browsers.
npm install p-limit
import pLimit from 'p-limit';
const limit = pLimit(1);
const input = [
limit(() => fetchSomething('foo')),
limit(() => fetchSomething('bar')),
limit(() => doSomething())
];
// Only one promise is run at once
const result = await Promise.all(input);
console.log(result);
Returns a limit function.
Type: number | object
Minimum: 1
Concurrency limit.
You can pass a number or an options object with a concurrency property.
Type: boolean
Default: false
Reject pending promises with an AbortError when clearQueue() is called.
This is recommended if you await the returned promises, for example with Promise.all, so pending tasks do not remain unresolved after clearQueue().
import pLimit from 'p-limit';
const limit = pLimit({concurrency: 1});
Returns the promise returned by calling fn(...args).
Type: Function
Promise-returning/async function.
Any arguments to pass through to fn.
Support for passing arguments on to the fn is provided in order to be able to avoid creating unnecessary closures. You probably don't need this optimization unless you're pushing a lot of functions.
Warning: Avoid calling the same limit function inside a function that is already limited by it. This can create a deadlock where inner tasks never run. Use a separate limiter for inner tasks.
Process an iterable of inputs with limited concurrency.
The mapper function receives the item value and its index.
Returns a promise equivalent to Promise.all(Array.from(iterable, (item, index) => limit(mapperFunction, item, index))).
This is a convenience function for processing inputs that arrive in batches. For more complex use cases, see p-map.
The number of promises that are currently running.
The number of promises that are waiting to run (i.e. their internal fn was not called yet).
Discard pending promises that are waiting to run.
This might be useful if you want to teardown the queue at the end of your program's lifecycle or discard any function calls referencing an intermediary state of your app.
Note: This does not cancel promises that are already running.
When rejectOnClear is enabled, pending promises are rejected with an AbortError.
This is recommended if you await the returned promises, for example with Promise.all, so pending tasks do not remain unresolved after clearQueue().
Get or set the concurrency limit.
Returns a function with limited concurrency.
The returned function manages its own concurrent executions, allowing you to call it multiple times without exceeding the specified concurrency limit.
Ideal for scenarios where you need to control the number of simultaneous executions of a single function, rather than managing concurrency across multiple functions.
import {limitFunction} from 'p-limit';
const limitedFunction = limitFunction(async () => {
return doSomething();
}, {concurrency: 1});
const input = Array.from({length: 10}, limitedFunction);
// Only one promise is run at once.
await Promise.all(input);
Type: Function
Promise-returning/async function.
Type: object
Type: number
Minimum: 1
Concurrency limit.
Type: boolean
Default: false
Reject pending promises with an AbortError when clearQueue() is called.
This is recommended if you await the returned promises, for example with Promise.all, so pending tasks do not remain unresolved after clearQueue().
See recipes.md for common use cases and patterns.
p-queue package?This package is only about limiting the number of concurrent executions, while p-queue is a fully featured queue implementation with lots of different options, introspection, and ability to pause the queue.