p-map vs async
Asynchronous Control Flow Libraries
p-mapasyncSimilar Packages:

Asynchronous Control Flow Libraries

Asynchronous control flow libraries in JavaScript help manage the execution of asynchronous operations, such as callbacks, promises, and events, in a more structured and manageable way. These libraries provide tools and patterns to handle tasks like sequencing, parallelism, and error handling, making it easier to write clean and maintainable code in environments like Node.js and the browser. They are particularly useful for avoiding callback hell, managing complex workflows, and improving the readability of asynchronous code. async is a comprehensive library that offers a wide range of utilities for managing asynchronous operations, including functions for parallel and serial execution, batching, and more. It provides a rich set of features for controlling the flow of asynchronous tasks, making it suitable for complex workflows. p-map, on the other hand, is a lightweight library focused on mapping over asynchronous iterable data with a specified concurrency limit. It allows you to process items in parallel while controlling the number of concurrent operations, making it ideal for scenarios where you want to limit resource usage or avoid overwhelming an API with too many simultaneous requests.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
p-map67,499,5811,48121.3 kB114 months agoMIT
async028,198808 kB242 years agoMIT

Feature Comparison: p-map vs async

Functionality

  • p-map:

    p-map focuses specifically on mapping over asynchronous iterables with a concurrency limit. It provides a single function, pMap, that takes an iterable, a mapping function, and a concurrency limit, making it simple to use for tasks that require controlled parallelism.

  • async:

    The async library provides a wide range of functions for managing asynchronous operations, including async.series, async.parallel, async.waterfall, and more. It supports both callback-based and promise-based APIs, allowing for flexible integration with different asynchronous patterns.

Concurrency Control

  • p-map:

    p-map is designed with concurrency control as a core feature. It allows you to specify the maximum number of concurrent operations, making it easy to limit resource usage and prevent overwhelming APIs or systems.

  • async:

    While async provides concurrency control in functions like async.parallelLimit, it is more of a general-purpose library without a specific focus on concurrency. Users can implement various concurrency patterns using its extensive feature set.

Ease of Use

  • p-map:

    p-map is straightforward and easy to use, especially for developers familiar with the Array.prototype.map method. Its simplicity and focused functionality make it quick to learn and integrate into projects.

  • async:

    async has a steeper learning curve due to its comprehensive feature set and the need to understand various patterns (e.g., series, parallel, waterfall). However, its flexibility and power make it worth the investment for complex tasks.

Size and Performance

  • p-map:

    p-map is a lightweight library with a small footprint, making it ideal for performance-sensitive applications where only a limited set of functionality is needed.

  • async:

    The async library is relatively large due to its extensive feature set. However, it is highly optimized for performance and provides efficient implementations of various asynchronous patterns.

Example Code

  • p-map:

    Here is an example of using p-map to process an array of URLs with controlled concurrency:

    const pMap = require('p-map');
    const fetch = require('node-fetch');
    
    const urls = [
      'https://jsonplaceholder.typicode.com/posts/1',
      'https://jsonplaceholder.typicode.com/posts/2',
      'https://jsonplaceholder.typicode.com/posts/3',
    ];
    
    const fetchUrl = async (url) => {
      const response = await fetch(url);
      return response.json();
    };
    
    const concurrencyLimit = 2;
    pMap(urls, fetchUrl, { concurrency: concurrencyLimit }).then((results) => {
      console.log('Fetched data:', results);
    });
    
  • async:

    Here is an example of using async to execute tasks in parallel:

    const async = require('async');
    
    const tasks = [
      (callback) => setTimeout(() => callback(null, 'Task 1 completed'), 1000),
      (callback) => setTimeout(() => callback(null, 'Task 2 completed'), 500),
      (callback) => setTimeout(() => callback(null, 'Task 3 completed'), 2000),
    ];
    
    async.parallel(tasks, (err, results) => {
      if (err) {
        console.error('Error executing tasks:', err);
      } else {
        console.log('All tasks completed:', results);
      }
    });
    

How to Choose: p-map vs async

  • p-map:

    Choose p-map if you need a simple and efficient way to map over asynchronous data with controlled concurrency. It is lightweight and easy to use, making it ideal for tasks like processing arrays or iterables with a limit on the number of concurrent operations.

  • async:

    Choose async if you need a comprehensive toolkit for managing various asynchronous patterns, including parallel, serial, and batched execution. It is suitable for complex workflows and provides extensive features for error handling and flow control.

README for p-map

p-map

Map over promises concurrently

Useful when you need to run promise-returning & async functions multiple times with different inputs concurrently.

This is different from Promise.all() in that you can control the concurrency and also decide whether or not to stop iterating when there's an error.

Install

npm install p-map

Usage

import pMap from 'p-map';
import got from 'got';

const sites = [
	getWebsiteFromUsername('sindresorhus'), //=> Promise
	'https://avajs.dev',
	'https://github.com'
];

const mapper = async site => {
	const {requestUrl} = await got.head(site);
	return requestUrl;
};

const result = await pMap(sites, mapper, {concurrency: 2});

console.log(result);
//=> ['https://sindresorhus.com/', 'https://avajs.dev/', 'https://github.com/']

API

pMap(input, mapper, options?)

Returns a Promise that is fulfilled when all promises in input and ones returned from mapper are fulfilled, or rejects if any of the promises reject. The fulfilled value is an Array of the fulfilled values returned from mapper in input order.

pMapIterable(input, mapper, options?)

Returns an async iterable that streams each return value from mapper in order.

import {pMapIterable} from 'p-map';

// Multiple posts are fetched concurrently, with limited concurrency and backpressure
for await (const post of pMapIterable(postIds, getPostMetadata, {concurrency: 8})) {
	console.log(post);
};

input

Type: AsyncIterable<Promise<unknown> | unknown> | Iterable<Promise<unknown> | unknown>

Synchronous or asynchronous iterable that is iterated over concurrently, calling the mapper function for each element. Each iterated item is await'd before the mapper is invoked so the iterable may return a Promise that resolves to an item.

Asynchronous iterables (different from synchronous iterables that return Promise that resolves to an item) can be used when the next item may not be ready without waiting for an asynchronous process to complete and/or the end of the iterable may be reached after the asynchronous process completes. For example, reading from a remote queue when the queue has reached empty, or reading lines from a stream.

mapper(element, index)

Type: Function

Expected to return a Promise or value.

options

Type: object

concurrency

Type: number (Integer)
Default: Infinity
Minimum: 1

Number of concurrently pending promises returned by mapper.

backpressure

Only for pMapIterable

Type: number (Integer)
Default: options.concurrency
Minimum: options.concurrency

Maximum number of promises returned by mapper that have resolved but not yet collected by the consumer of the async iterable. Calls to mapper will be limited so that there is never too much backpressure.

Useful whenever you are consuming the iterable slower than what the mapper function can produce concurrently. For example, to avoid making an overwhelming number of HTTP requests if you are saving each of the results to a database.

stopOnError

Only for pMap

Type: boolean
Default: true

When true, the first mapper rejection will be rejected back to the consumer.

When false, instead of stopping when a promise rejects, it will wait for all the promises to settle and then reject with an AggregateError containing all the errors from the rejected promises.

Caveat: When true, any already-started async mappers will continue to run until they resolve or reject. In the case of infinite concurrency with sync iterables, all mappers are invoked on startup and will continue after the first rejection. Issue #51 can be implemented for abort control.

signal

Only for pMap

Type: AbortSignal

You can abort the promises using AbortController.

import pMap from 'p-map';
import delay from 'delay';

const abortController = new AbortController();

setTimeout(() => {
	abortController.abort();
}, 500);

const mapper = async value => value;

await pMap([delay(1000), delay(1000)], mapper, {signal: abortController.signal});
// Throws AbortError (DOMException) after 500 ms.

pMapSkip

Return this value from a mapper function to skip including the value in the returned array.

import pMap, {pMapSkip} from 'p-map';
import got from 'got';

const sites = [
	getWebsiteFromUsername('sindresorhus'), //=> Promise
	'https://avajs.dev',
	'https://example.invalid',
	'https://github.com'
];

const mapper = async site => {
	try {
		const {requestUrl} = await got.head(site);
		return requestUrl;
	} catch {
		return pMapSkip;
	}
};

const result = await pMap(sites, mapper, {concurrency: 2});

console.log(result);
//=> ['https://sindresorhus.com/', 'https://avajs.dev/', 'https://github.com/']

Related

  • p-all - Run promise-returning & async functions concurrently with optional limited concurrency
  • p-filter - Filter promises concurrently
  • p-times - Run promise-returning & async functions a specific number of times concurrently
  • p-props - Like Promise.all() but for Map and Object
  • p-map-series - Map over promises serially
  • More…