p-limit vs async vs promise-queue vs bluebird vs promise-limit
JavaScript Promise and Async Control Libraries
p-limitasyncpromise-queuebluebirdpromise-limitSimilar Packages:

JavaScript Promise and Async Control Libraries

JavaScript Promise and Async Control Libraries are tools that help manage asynchronous operations in JavaScript, providing more control over how and when these operations are executed. They offer features like limiting concurrency, queuing tasks, and handling promises more efficiently, which can improve performance and resource management in applications. These libraries are particularly useful in scenarios where you need to perform multiple asynchronous tasks but want to avoid overwhelming the system or running into issues like callback hell or unhandled promise rejections. They provide a more structured and manageable way to work with asynchronous code, making it easier to write, read, and maintain.

Npm Package Weekly Downloads Trend

3 Years

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
p-limit442,781,588-16.1 kB-2 months agoMIT
async144,003,447-808 kB-2 years agoMIT
promise-queue1,235,228---9 years agoMIT
bluebird0---7 years agoMIT
promise-limit0---8 years agoISC

Feature Comparison: p-limit vs async vs promise-queue vs bluebird vs promise-limit

Concurrency Control

  • p-limit:

    p-limit is specifically designed for limiting the concurrency of promise-based tasks. You can create a limit function that enforces a maximum number of concurrent promises, making it simple to control resource usage.

  • async:

    The async library provides various functions for controlling concurrency, such as async.parallelLimit and async.eachLimit, allowing you to specify limits on how many tasks run simultaneously.

  • promise-queue:

    promise-queue manages concurrency by queuing tasks and executing them in order with a specified limit on how many run simultaneously. This ensures that tasks are completed in the order they were added.

  • bluebird:

    bluebird offers concurrency control through its Promise.map method, which allows you to set a concurrency limit while mapping over an array of values, executing promises in parallel up to the specified limit.

  • promise-limit:

    promise-limit provides a straightforward way to limit the number of concurrent promises. It exports a function that takes a limit and returns a wrapper function to control concurrency when executing promises.

Task Queuing

  • p-limit:

    p-limit does not provide task queuing features. It focuses solely on limiting concurrency for promise-based tasks without managing their execution order.

  • async:

    async supports task queuing through its queue and priorityQueue functions, allowing you to create queues with customizable concurrency and priority levels.

  • promise-queue:

    promise-queue is designed for task queuing, ensuring that tasks are executed in the order they are added to the queue. It allows you to set a concurrency limit while maintaining the order of execution.

  • bluebird:

    bluebird does not have built-in task queuing, but you can implement it using its promise chaining and mapping features. The library focuses more on promise management than queuing.

  • promise-limit:

    promise-limit does not include task queuing capabilities. It is designed for simple concurrency limiting without queuing tasks for later execution.

Error Handling

  • p-limit:

    p-limit handles errors in promise-based tasks by allowing them to be rejected as usual. It does not provide special error handling features but works seamlessly with standard promise error handling.

  • async:

    async provides robust error handling mechanisms, including support for error-first callbacks, try/catch in async functions, and the ability to handle errors in parallel and series tasks.

  • promise-queue:

    promise-queue handles errors by allowing rejected promises to propagate through the queue. It does not provide specialized error handling but ensures that errors are managed in the order tasks are executed.

  • bluebird:

    bluebird offers advanced error handling features, including promise cancellation, error propagation, and the ability to catch errors at different stages of promise execution. It also supports unhandled rejection tracking.

  • promise-limit:

    promise-limit allows errors to be handled by the promises it manages. It does not introduce any special error handling mechanisms, relying on standard promise behavior.

Performance

  • p-limit:

    p-limit is lightweight and introduces minimal overhead when limiting concurrency. It is designed for performance, making it suitable for scenarios where you need to control concurrency without significant impact on execution speed.

  • async:

    async is efficient for managing asynchronous operations, but its performance can vary depending on the complexity of the tasks and the concurrency settings. It is designed for flexibility rather than raw performance.

  • promise-queue:

    promise-queue is efficient in managing queued tasks, but its performance depends on the concurrency limit and the number of tasks. It is designed to balance order and concurrency without significant overhead.

  • bluebird:

    bluebird is one of the fastest promise libraries available, optimized for performance with features like lazy evaluation, efficient memory usage, and minimal overhead for promise creation and resolution.

  • promise-limit:

    promise-limit is designed to be simple and efficient, with low overhead for limiting concurrent promise execution. It is suitable for performance-sensitive applications that require basic concurrency control.

Ease of Use: Code Examples

  • p-limit:

    Concurrency control with p-limit library

    const pLimit = require('p-limit');
    
    const limit = pLimit(2); // Limit concurrency to 2
    const tasks = [
      () => new Promise((resolve) => setTimeout(() => resolve('Task 1 complete'), 1000)),
      () => new Promise((resolve) => setTimeout(() => resolve('Task 2 complete'), 500)),
      () => new Promise((resolve) => setTimeout(() => resolve('Task 3 complete'), 2000)),
    ];
    
    const limitedTasks = tasks.map((task) => limit(task));
    Promise.all(limitedTasks)
      .then((results) => console.log(results))
      .catch((err) => console.error(err));
    
  • async:

    Concurrency control with async library

    const async = require('async');
    
    const tasks = [
      (callback) => setTimeout(() => callback(null, 'Task 1 complete'), 1000),
      (callback) => setTimeout(() => callback(null, 'Task 2 complete'), 500),
      (callback) => setTimeout(() => callback(null, 'Task 3 complete'), 2000),
    ];
    
    // Limit concurrency to 2 tasks at a time
    async.parallelLimit(tasks, 2, (err, results) => {
      if (err) console.error(err);
      console.log(results);
    });
    
  • promise-queue:

    Task queuing with promise-queue library

    const PromiseQueue = require('promise-queue');
    
    const queue = new PromiseQueue(2, Infinity); // Limit concurrency to 2
    const tasks = [
      () => new Promise((resolve) => setTimeout(() => resolve('Task 1 complete'), 1000)),
      () => new Promise((resolve) => setTimeout(() => resolve('Task 2 complete'), 500)),
      () => new Promise((resolve) => setTimeout(() => resolve('Task 3 complete'), 2000)),
    ];
    
    // Add tasks to the queue
    const promises = tasks.map((task) => queue.add(task));
    Promise.all(promises)
      .then((results) => console.log(results))
      .catch((err) => console.error(err));
    
  • bluebird:

    Concurrency control with bluebird library

    const Promise = require('bluebird');
    
    const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
    const tasks = [
      () => delay(1000).then(() => 'Task 1 complete'),
      () => delay(500).then(() => 'Task 2 complete'),
      () => delay(2000).then(() => 'Task 3 complete'),
    ];
    
    // Limit concurrency to 2 tasks at a time
    Promise.map(tasks, (task) => task(), { concurrency: 2 })
      .then((results) => console.log(results))
      .catch((err) => console.error(err));
    
  • promise-limit:

    Concurrency control with promise-limit library

    const promiseLimit = require('promise-limit');
    
    const limit = promiseLimit(2); // Limit concurrency to 2
    const tasks = [
      () => new Promise((resolve) => setTimeout(() => resolve('Task 1 complete'), 1000)),
      () => new Promise((resolve) => setTimeout(() => resolve('Task 2 complete'), 500)),
      () => new Promise((resolve) => setTimeout(() => resolve('Task 3 complete'), 2000)),
    ];
    
    const limitedTasks = tasks.map((task) => limit(task));
    Promise.all(limitedTasks)
      .then((results) => console.log(results))
      .catch((err) => console.error(err));
    

How to Choose: p-limit vs async vs promise-queue vs bluebird vs promise-limit

  • p-limit:

    Choose p-limit if you need a simple and lightweight solution for limiting the concurrency of promise-based tasks. It is perfect for scenarios where you want to control the number of promises running simultaneously without adding much overhead.

  • async:

    Choose async if you need a comprehensive toolkit for managing asynchronous operations with a wide range of utilities, including parallel, series, and waterfall execution. It is ideal for complex workflows that require fine-grained control over task execution.

  • promise-queue:

    Choose promise-queue if you need a queue-based approach to managing asynchronous tasks, ensuring that they are executed in order and with a specified concurrency limit. It is ideal for scenarios where task order is important, and you want to prevent resource exhaustion.

  • bluebird:

    Choose bluebird if you want a high-performance promise library that offers advanced features like cancellation, progress tracking, and a rich set of utility methods. It is suitable for applications that require efficient promise handling and want to avoid the limitations of native promises.

  • promise-limit:

    Choose promise-limit if you want a straightforward implementation for limiting the number of concurrent promises. It is easy to use and integrates well with existing promise-based code, making it a good choice for projects that need basic concurrency control.

README for p-limit

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

If the iterable throws during iteration, the returned promise rejects with that error. Mapper functions that were already scheduled still run, and their results are discarded.

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

limitedFunction.clearQueue()

Discard pending calls that have not started yet.

When rejectOnClear is enabled, pending promises are rejected with an AbortError. Otherwise, their promises remain unresolved.

This does not cancel calls that are already running.

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…