p-map vs p-queue vs p-all vs p-series
Node.js 异步控制库
p-mapp-queuep-allp-series类似的npm包:
Node.js 异步控制库

在 Node.js 开发中,处理异步操作是一个常见的挑战。这些库提供了不同的方式来管理并发操作,帮助开发者更有效地处理异步任务。通过使用这些库,开发者可以更好地控制任务的执行顺序、并发数量和错误处理,从而提高代码的可读性和可维护性。

npm下载趋势
3 年
GitHub Stars 排名
统计详情
npm包名称
下载量
Stars
大小
Issues
发布时间
License
p-map27,082,6381,47121.3 kB112 个月前MIT
p-queue6,674,7334,07472.4 kB62 个月前MIT
p-all597,9343445.6 kB04 个月前MIT
p-series13,53371-04 年前MIT
功能对比: p-map vs p-queue vs p-all vs p-series

并发控制

  • p-map:

    p-map 允许你对数组中的每个元素进行异步操作,并且可以限制并发执行的数量,适合于需要控制并发的场景。

  • p-queue:

    p-queue 通过队列管理异步任务,确保任务按顺序执行,适合于需要严格控制执行顺序的场景。

  • p-all:

    p-all 允许同时执行多个 Promise,适合于不需要控制并发数量的场景。所有 Promise 完成后返回结果,适合于独立的异步操作。

  • p-series:

    p-series 按顺序执行多个异步操作,确保每个操作在前一个操作完成后再开始,适合于需要依赖前一个操作结果的场景。

错误处理

  • p-map:

    p-map 允许对每个异步操作进行单独的错误处理,适合于需要对每个操作的结果进行监控的场景。

  • p-queue:

    p-queue 可以在队列中处理错误,确保任务顺序执行,即使某个任务失败,后续任务仍然可以继续执行。

  • p-all:

    p-all 在所有 Promise 中的任何一个失败时,会立即返回错误,适合于不需要逐个处理错误的场景。

  • p-series:

    p-series 在执行过程中,如果某个操作失败,后续操作将不会执行,适合于需要确保每个操作成功的场景。

使用场景

  • p-map:

    适用于需要对数组中的每个元素进行异步操作,并希望控制并发数量的场景,例如批量处理数据。

  • p-queue:

    适用于需要管理异步任务执行顺序的场景,例如处理消息队列。

  • p-all:

    适用于需要同时处理多个独立异步操作的场景,例如并行请求多个 API。

  • p-series:

    适用于需要依赖前一个操作结果的场景,例如依次执行数据库操作。

性能

  • p-map:

    p-map 通过限制并发数量,可以有效控制资源使用,避免性能问题。

  • p-queue:

    p-queue 通过顺序执行任务,可能会导致整体执行时间增加,但可以确保资源的合理使用。

  • p-all:

    p-all 在处理大量独立异步操作时可能会导致性能瓶颈,因为它会同时启动所有 Promise,可能会导致资源耗尽。

  • p-series:

    p-series 由于按顺序执行,可能会导致整体执行时间较长,但可以确保每个操作的成功。

学习曲线

  • p-map:

    p-map 需要理解如何限制并发,学习曲线稍微陡峭,但提供了更强的控制能力。

  • p-queue:

    p-queue 的概念相对简单,容易理解,适合需要顺序执行的场景。

  • p-all:

    p-all 的使用相对简单,适合快速上手,但在复杂场景下可能需要更多的错误处理逻辑。

  • p-series:

    p-series 的使用也很简单,适合初学者,特别是在需要依赖前一个操作结果的场景中。

如何选择: p-map vs p-queue vs p-all vs p-series
  • p-map:

    选择 p-map 如果你需要对数组中的每个元素执行异步操作,并希望限制并发执行的数量。它适合于需要控制并发的场景,例如 API 请求。

  • p-queue:

    选择 p-queue 如果你需要一个队列来管理异步任务的执行顺序,确保任务按顺序执行,适合于需要严格控制执行顺序的场景。

  • p-all:

    选择 p-all 如果你需要并行执行多个 Promise,并且希望在所有 Promise 完成后获得结果。它适合于不需要控制并发数量的场景。

  • p-series:

    选择 p-series 如果你希望按顺序执行多个异步操作,确保每个操作在前一个操作完成后再开始。适合于需要依赖前一个操作结果的场景。

p-map的README

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…