p-limit vs p-queue vs p-throttle
非同期処理の並行性とレート制限の制御
p-limitp-queuep-throttle類似パッケージ:

非同期処理の並行性とレート制限の制御

p-limitp-queuep-throttle は、JavaScript における非同期処理のフローを制御するためのユーティリティです。p-limit は同時実行数(コネクション数など)を制限し、リソースの枯渇を防ぎます。p-throttle は単位時間あたりの実行回数を制限し、API のレート制限対策などに有効です。p-queue はこれらを統合した優先度付きキューであり、タスクの順序制御やイベント監視も可能です。

npmのダウンロードトレンド

3 年

GitHub Starsランキング

統計詳細

パッケージ
ダウンロード数
Stars
サイズ
Issues
公開日時
ライセンス
p-limit217,928,5302,81814.9 kB01ヶ月前MIT
p-queue21,131,0754,14576.8 kB52ヶ月前MIT
p-throttle3,160,00451321.6 kB04ヶ月前MIT

p-limit vs p-queue vs p-throttle: 非同期フローの制御比較

JavaScript で非同期処理を扱う際、Promise.all() や単純な async/await だけでは制御しきれない場面があります。サーバーへの負荷軽減、API のレート制限遵守、リソースの競合回避 — これらの課題を解決するのが p-limitp-queuep-throttle です。これらは似ていますが、解決する問題が異なります。

🚀 核心の違い:並行性 vs レート vs キュー

p-limit は「同時実行数」を制限します。

  • 一度に何個の Promise を実行するか決めます。
  • 超えた分はキューに溜まり、空き次第実行されます。
  • 時間制限はありません。
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 は「単位時間あたりの実行回数」を制限します。

  • 1 秒間に 1 回など、時間間隔を基準にします。
  • 同時実行数というより、頻度(レート)を制御します。
  • API のレート制限エラーを防ぐのに最適です。
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 は「優先度付きキュー」です。

  • 並行性制限と時間制限(intervalCap)の両方を持てます。
  • タスクに優先度を付けられます。
  • キューが空になったなどのイベントを監視できます。
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-limitp-throttlep-queue
同時実行数制限
時間ベース制限
優先順位
イベント監視
一時停止/再開

優先順位の重要性

p-queue だけがタスクに優先度を付けられます。例えば、ユーザーの操作に関連するリクエストを、バックグラウンドの同期処理より先に処理したい場合に有効です。

// p-queue の場合
queue.add(criticalTask, { priority: 100 });
queue.add(backgroundTask, { priority: 1 });

// p-limit や p-throttle では順序制御ができない

イベント監視の利点

p-queueonEmptyonIdle などのイベントを提供します。バッチ処理の完了を検知して次の処理に進むような場合に便利です。

// p-queue の場合
queue.on('empty', () => {
  console.log('キューが空になりました');
});

// p-limit や p-throttle では外部で管理が必要

🌐 実世界のシナリオ

シナリオ 1: 画像の批量読み込み

多数の画像を同時に読み込むとブラウザが固まります。同時実行数を制限します。

  • 最佳選択: p-limit
  • 理由: 単純に同時接続数を抑えたいだけ。優先順位は不要。
const limit = pLimit(5);
const images = urls.map(url => limit(() => loadImg(url)));
await Promise.all(images);

シナリオ 2: 外部 API の呼び出し

無料枠の API は「1 秒間に 10 リクエスト」などの制限があります。

  • 最佳選択: p-throttle
  • 理由: 時間あたりの回数制限を厳守する必要があるため。
const throttle = pThrottle({ limit: 10, interval: 1000 });
const callApi = throttle(() => fetch('/external'));
await callApi();

シナリオ 3: 複雑なバッチ処理システム

重要なタスクと重要でないタスクが混在し、全体の実行速度も制限したい。

  • 最佳選択: p-queue
  • 理由: 優先順位付けと、並行性・レート制限の両方が必要なため。
const 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 は本格的なタスク管理 — 優先度やイベント監視が必要な複雑なシステムに。

プロジェクトの要件に合わせて適切なものを選ぶことで、安定した非同期処理を実装できます。

選び方: p-limit vs p-queue vs p-throttle

  • p-limit:

    単純に同時実行数を抑えたい場合に最適です。依存関係が少なく、バンドルサイズを小さく保ちたいプロジェクトに向いています。複雑なキュー管理が必要ない限り、最も軽量な選択肢となります。

  • p-queue:

    優先順位付けや、並行性とレート制限の両方が必要な場合に選択します。タスクの完了イベントを監視したい場合にも有用です。大規模なバッチ処理や、タスクの優先度を変えたい場景で真価を発揮します。

  • p-throttle:

    API のレート制限など、時間ベースの制限を厳密に守る必要がある場合に使用します。間隔を空けて実行したい処理に最適です。単純な関数の呼び出し頻度を制御したい場合に適しています。

p-limit のREADME

p-limit

Run multiple promise-returning & async functions with limited concurrency

Works in Node.js and browsers.

Install

npm install p-limit

Usage

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);

API

pLimit(concurrency) default export

Returns a limit function.

concurrency

Type: number | object
Minimum: 1

Concurrency limit.

You can pass a number or an options object with a concurrency property.

rejectOnClear

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});

limit(fn, ...args)

Returns the promise returned by calling fn(...args).

fn

Type: Function

Promise-returning/async function.

args

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.

limit.map(iterable, mapperFunction)

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.

limit.activeCount

The number of promises that are currently running.

limit.pendingCount

The number of promises that are waiting to run (i.e. their internal fn was not called yet).

limit.clearQueue()

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().

limit.concurrency

Get or set the concurrency limit.

limitFunction(fn, options) named export

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);

fn

Type: Function

Promise-returning/async function.

options

Type: object

concurrency

Type: number
Minimum: 1

Concurrency limit.

rejectOnClear

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().

Recipes

See recipes.md for common use cases and patterns.

FAQ

How is this different from the 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.

Related

  • p-throttle - Throttle promise-returning & async functions
  • p-debounce - Debounce promise-returning & async functions
  • p-map - Run promise-returning & async functions concurrently with different inputs
  • p-all - Run promise-returning & async functions concurrently with optional limited concurrency
  • More…