当前位置: 首页 > article >正文

Node.js 异步并发控制:`p-map` 和 `p-limit` 的使用与对比

在 Node.js 中,处理异步任务是开发中非常常见的需求。无论是批量处理数据、调用外部 API,还是操作文件系统,我们经常需要对多个异步任务进行管理。然而,当任务数量较多时,如果不加以控制,并发可能会导致性能问题甚至崩溃。为了更好地管理异步任务的并发,社区提供了两个非常流行的工具库:p-mapp-limit

本文将详细介绍这两个工具库的功能、用法以及它们之间的区别,并通过实际代码示例帮助你理解如何选择合适的工具来满足你的需求。


1. 什么是 p-map

核心功能

p-map 是一个用于对数组中的每个元素执行异步操作的工具库。它的主要特点是:

  • 提供了类似于 Array.prototype.map() 的 API。
  • 内置并发控制,允许你限制同时运行的任务数量。
  • 支持灵活的错误处理机制。

基本用法

const pMap = require('p-map');async function main() {const numbers = [1, 2, 3, 4, 5];const results = await pMap(numbers, async (num) => {return num * 2; // 模拟异步操作}, { concurrency: 2 }); // 并发数为 2console.log(results); // 输出: [2, 4, 6, 8, 10]
}main();

在这个例子中,pMap 对数组 [1, 2, 3, 4, 5] 中的每个元素执行了异步操作(乘以 2),并且并发数限制为 2。


错误处理

p-map 提供了一个非常有用的选项 stopOnError,用于控制在遇到错误时是否停止后续任务的执行。

默认行为(stopOnError: true

如果某个任务失败,p-map 会立即停止并抛出错误。

const pMap = require('p-map');async function main() {const numbers = [1, 2, 3, 4, 5];try {const results = await pMap(numbers, async (num) => {if (num === 3) {throw new Error('Error on number 3');}return num * 2;}, { concurrency: 2 });console.log(results);} catch (error) {console.error('Caught error:', error.message); // 输出: Caught error: Error on number 3}
}main();
忽略错误(stopOnError: false

即使某个任务失败,p-map 也会继续执行剩余的任务。失败的任务会返回 undefined

const pMap = require('p-map');async function main() {const numbers = [1, 2, 3, 4, 5];const results = await pMap(numbers, async (num) => {if (num === 3) {throw new Error('Error on number 3');}return num * 2;}, { concurrency: 2, stopOnError: false });console.log(results); // 输出: [2, 4, undefined, 8, 10]
}main();
自定义错误处理

你可以通过 try-catch 捕获错误,并根据需要返回默认值或记录日志。

const pMap = require('p-map');async function main() {const numbers = [1, 2, 3, 4, 5];const results = await pMap(numbers, async (num) => {try {if (num === 3) {throw new Error('Error on number 3');}return num * 2;} catch (error) {console.error(`Task failed for number ${num}:`, error.message);return null; // 返回一个默认值}}, { concurrency: 2 });console.log(results); // 输出: [2, 4, null, 8, 10]
}main();

2. 什么是 p-limit

核心功能

p-limit 是一个更通用的并发控制工具,它允许你创建一个并发限制器,用于控制任意异步任务的并发数。与 p-map 不同,p-limit 不局限于数组操作,适用于任何需要并发控制的异步任务。

基本用法

const pLimit = require('p-limit');async function main() {const limit = pLimit(2); // 并发数为 2const tasks = [limit(() => fetchSomething('task1')),limit(() => fetchSomething('task2')),limit(() => fetchSomething('task3')),limit(() => fetchSomething('task4'))];const results = await Promise.all(tasks);console.log(results);
}async function fetchSomething(name) {console.log(`Fetching ${name}`);// 模拟异步操作return new Promise(resolve => setTimeout(() => resolve(name), 1000));
}main();

在这个例子中,p-limit 创建了一个并发限制器,限制了同时运行的任务数量为 2。


优点

  • 灵活性高:不局限于数组映射,可以用于任何异步任务。
  • 手动控制任务:你可以根据需要动态地添加任务,并且可以将任务分组或按需执行。
  • 适合复杂场景:例如,当你需要从不同的数据源获取数据并限制并发时,p-limit 更加适合。

3. p-map vs p-limit

特性p-mapp-limit
核心功能对数组进行异步映射控制任意异步任务的并发数
适用场景数组映射、批量处理异步任务复杂任务队列、动态任务管理
并发控制内置并发控制(通过 concurrency 参数)手动创建并发限制器
错误处理内置错误处理(通过 stopOnError 参数)需要手动处理错误
灵活性专注于数组映射,灵活性较低高度灵活,适用于各种异步任务
API 简洁性API 简洁,易于使用API 较为底层,需要手动包装任务

4. 如何选择?

使用 p-map 的场景

  • 当你需要对一个数组进行异步映射操作时。
  • 当你希望自动管理并发数,并且不需要手动控制任务队列时。
  • 当你需要简单的错误处理机制(如 stopOnError)。
示例场景:
  • 批量下载文件列表。
  • 对数据库中的记录进行批量更新。

使用 p-limit 的场景

  • 当你需要对多个异步任务进行并发控制,但这些任务并不一定来自数组。
  • 当你需要手动管理任务队列,或者任务是动态生成的。
  • 当你需要更高的灵活性来处理复杂的异步任务。
示例场景:
  • 从多个 API 获取数据,并限制并发请求数。
  • 动态生成任务并逐步执行。

5. 结合使用

在某些情况下,你可以结合使用 p-mapp-limit。例如,你可以使用 p-limit 来创建一个并发限制器,然后将其与 p-map 结合使用。

const pMap = require('p-map');
const pLimit = require('p-limit');async function main() {const limit = pLimit(2); // 并发数为 2const numbers = [1, 2, 3, 4, 5];const results = await pMap(numbers, (num) => limit(() => asyncOperation(num)));console.log(results);
}async function asyncOperation(num) {// 模拟异步操作return new Promise(resolve => setTimeout(() => resolve(num * 2), 1000));
}main();

总结

  • 如果你的需求是对数组进行异步映射操作,p-map 是更好的选择,因为它提供了简洁的 API 和内置的并发控制。
  • 如果你需要更灵活的任务管理,或者任务不是来自数组,p-limit 是更合适的选择,它提供了更高的灵活性和并发控制能力。

根据具体的需求选择合适的工具,可以让代码更加简洁和高效!

相关文章:

Node.js 异步并发控制:`p-map` 和 `p-limit` 的使用与对比

在 Node.js 中,处理异步任务是开发中非常常见的需求。无论是批量处理数据、调用外部 API,还是操作文件系统,我们经常需要对多个异步任务进行管理。然而,当任务数量较多时,如果不加以控制,并发可能会导致性能…...

【c++】c++内存管理

目录 c和c的内存分布回顾C语言动态管理内存的方式malloccallocreallocfree C动态管理内存的方式new和deleteoperator new和operator delete定位new c和c的内存分布 回顾C语言动态管理内存的方式 malloc void* malloc (size_t size);malloc可以在堆上开辟指定内存的空间&#…...

EtherNet/IP转Modbus TCP:新能源风电监控与分析实用案例

EtherNet/IP转Modbus TCP:新能源风电监控与分析实用案例 一、案例背景 在某新能源汽车电池生产线上,需要将采用EtherNet/IP协议的电池检测设备与采用ProfiNet协议的生产线控制系统进行集成,以实现对电池生产过程的全面监控和数据采集。 二、…...

伪装目标检测(Camouflaged Object Detection, COD)教程

1. 引言 伪装目标检测(Camouflaged Object Detection, COD)是一项计算机视觉任务,旨在识别和分割背景中难以察觉的目标,如动物伪装、隐形物体检测等。由于伪装目标通常与背景高度相似,这项任务比传统的目标检测更具挑…...

烧烤炉出口亚马逊欧盟站CE认证EN1860安全标准

什么是欧盟CE认证: 在欧盟市场“CE”标志属强制性认证标志,不论是欧盟内部企业生产的产品,还是其他国家生产的产品,要想在欧盟市场上自由流通,就必须加贴“CE”标志,以表明产品符合欧盟《技术协调与标准化新…...

动态DNS神器nip.io使用指南:快速实现域名与IP的动态映射--告别配置本地hosts

动态DNS神器nip.io使用指南:快速实现域名与IP的动态映射--告别配置本地hosts 一、项目简介二、快速入门三、进阶配置四、典型应用场景 本文基于开源项目 v1.2.1版本撰写,适用于开发测试、CI/CD等场景 一、项目简介 nip.io 是由Exentrique Solutions开发…...

人工智能 - 机器学习、深度学习、强化学习是人工智能领域的理论基础和方法论

机器学习、深度学习、强化学习是人工智能领域的三大核心方向,各自具有独特的理论基础和方法论。以下是它们的核心理论知识总结: 一、机器学习(Machine Learning, ML) 1. 基础概念 目标:通过数据驱动的方式,让机器从经验中学习规律,完成预测、分类或决策任务。 核心范式…...

数字电路-基础逻辑门实验

基础逻辑门是数字电路设计的核心元件,它们执行的是基本的逻辑运算。通过这些基本运算,可以构建出更为复杂的逻辑功能。常见的基础逻辑门包括与门(AND)、或门(OR)、非门(NOT)、异或门…...

国产编辑器EverEdit - 如虎添翼的功能:快速选择

1 快速选择 1.1 应用场景 快速选择适用于批量选择和修改的场景,比如:变量改名。 1.2 使用方法 1.2.1 逐项快速选择 将光标放置在单词前或单词中,选择主菜单查找 -> 快速选择 -> 快速选择或使用快捷键Ctrl D 注:光标放…...

国内外网络安全政策动态(2025年1月)

▶︎ 1.国家互联网信息办公室发布《个人信息出境个人信息保护认证办法(征求意见稿)》 1月3日,国家互联网信息办公室发布《个人信息出境个人信息保护认证办法(征求意见稿)》。根据《意见稿》,个人信息出境个…...

68页PDF | 数据安全总体解决方案:从数据管理方法论到落地实践的全方位指南(附下载)

一、前言 这份报告旨在应对数字化转型过程中数据安全面临的挑战,并提供全面的管理与技术体系建设框架。报告首先分析了数字化社会的发展背景,强调了数据安全在国家安全层面的重要性,并指出数据安全风险的来源和防护措施。接着,报…...

AI大模型的文本流如何持续吐到前端,实时通信的技术 SSE(Server-Sent Events) 认知

写在前面 没接触过 SSE(Server-Sent Events),AI大模型出来之后,一直以为文本流是用 WebSocket 做的偶然看到返回到报文格式是 text/event-stream,所以简单认知,整理笔记博文内容涉及 SSE 认知,以及对应的 D…...

Electron:使用electron-react-boilerplate创建一个react + electron的项目

使用 electron-react-boilerplate git clone --depth 1 --branch main https://github.com/electron-react-boilerplate/electron-react-boilerplate.git your-project-name cd your-project-name npm install npm start 安装不成功 在根目录加上 .npmrc文件 内容为 electron_…...

Spring Boot三:Springboot自动装配原理

精心整理了最新的面试资料&#xff0c;有需要的可以自行获取 点击前往百度网盘获取 点击前往夸克网盘获取 原理初探 pom.xml 核心依赖在父工程中 spring-boot-dependencies所有的jar包都在这里管理 我们在写或者引入一些依赖的时候&#xff0c;不需要指定版本 启动器 <…...

【ISO 14229-1:2023 UDS诊断全量测试用例清单系列:第十八节】

ISO 14229-1:2023 UDS诊断服务测试用例全解析&#xff08;ResponseOnEvent_0x86服务&#xff09; 作者&#xff1a;车端域控测试工程师 更新日期&#xff1a;2025年02月14日 关键词&#xff1a;UDS协议、0x86服务、事件响应、ISO 14229-1:2023、ECU测试 一、服务功能概述 0x86…...

Qt 中使用 SQLite 数据库的完整指南

SQLite 是一款轻量级、嵌入式的关系型数据库&#xff0c;无需独立的服务器进程&#xff0c;数据以文件形式存储&#xff0c;非常适合桌面和移动端应用的本地数据管理。Qt 通过 Qt SQL 模块提供了对 SQLite 的原生支持&#xff0c;开发者可以轻松实现数据库的增删改查、事务处理…...

2024 年 CSDN 博客之星年度评选:技术创作与影响力的碰撞(统计时间2025-02-17 11:06:06)

摘要&#xff1a;在技术的海洋里&#xff0c;每一位博主都像是一座独特的灯塔&#xff0c;用自己创作的光芒照亮他人前行的道路。2024 年 CSDN 博客之星年度评选活动&#xff0c;正是对这些灯塔的一次盛大检阅&#xff0c;让我们看到了众多优秀博主在技术创作领域的卓越表现以及…...

Java零基础入门笔记:(3)程序控制

前言 本笔记是学习狂神的java教程&#xff0c;建议配合视频&#xff0c;学习体验更佳。 【狂神说Java】Java零基础学习视频通俗易懂_哔哩哔哩_bilibili Scanner对象 之前我们学的基本语法中我们并没有实现程序和人的交互&#xff0c;但是Java给我们提供了这样一个工具类&…...

后端生成二维码,前端请求接口生成二维码并展示,且多个参数后边的参数没有正常传输问题处理

一、后端代码 1、controller GetMapping("/generateQRCode/{url}")ApiOperation(value "生成url链接二维码",notes "生成url链接二维码")public JsonResult<NewsQRCodeVo> generateQRCode(PathVariable String url,HttpServletRespons…...

(8/100)每日小游戏平台系列

项目地址位于&#xff1a;小游戏导航 新增一个打地鼠游戏&#xff01; 打地鼠&#xff08;Whack-a-Mole&#xff09;是一款经典的休闲游戏&#xff0c;玩家需要点击随机出现的地鼠&#xff0c;以获取分数。游戏时间有限&#xff0c;玩家需要在规定时间内尽可能多地击中地鼠&am…...

【jar包启动命令简单分享】

最近在做springcloud项目&#xff0c;整理了下启停脚本 批量启动脚本 #!/bin/bashAPP_HOME/data/java/ APP_NAMES("ruoyi-auth.jar""ruoyi-gateway.jar""ruoyi-modules-file.jar""ruoyi-modules-gen.jar""ruoyi-modules-job.jar…...

[Python人工智能] 五十.PyTorch入门 (5)快速搭建神经网络及模型保存

从本专栏开始,作者正式研究Python深度学习、神经网络及人工智能相关知识。前文讲解PyTorch构建分类神经网络。这篇文章将介绍如何利用PyTorch快速构建神经网络,之前的代码比较复杂,通过自定义Net类实现,本文通过Torch函数定义神经网络。前面我们的Python人工智能主要以Tens…...

SpringBoot+Vue+数据可视化的动漫妆造服务平台(程序+论文+讲解+安装+调试+售后等)

感兴趣的可以先收藏起来&#xff0c;还有大家在毕设选题&#xff0c;项目以及论文编写等相关问题都可以给我留言咨询&#xff0c;我会一一回复&#xff0c;希望帮助更多的人。 系统介绍 在当今数字化高速发展的时代&#xff0c;动漫产业迎来了前所未有的繁荣&#xff0c;动漫…...

Go入门之语言变量 常量介绍

func main(){var a int8 10var b int 5var c int 6fmt.Println("a", a, "b", b, "c", c)d : 10fmt.Printf("a%v leixing%T\n", d, d) } main函数是入口函数,fmt包有三个打印的函数Println&#xff0c;Print&#xff0c;Printf。第…...

基于web的留守儿童网站的设计与实现

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…...

鸡兔同笼问题

鸡兔同笼问题是这样一个问题&#xff1a; 现有鸡、兔合装在一个笼子里。数头一共100个头&#xff0c;数脚一共300只脚。问有多少只鸡多少只兔&#xff1f; 在这里讨论这个问题的解法当然太小儿科了。但是y_tab这个C语言解释器只提供了1维数组。如果需要用到2维数组时&#xff…...

使用 Spring Boot 和 Canal 实现 MySQL 数据库同步

文章目录 前言一、背景二、Canal 简介三、主库数据库配置1.主库配置2.创建 Canal 用户并授予权限 四.配置 Canal Server1.Canal Server 配置文件2.启动 Canal Server 五.开发 Spring Boot 客户端1. 引入依赖2. 配置 Canal 客户端3. 实现数据同步逻辑 六.启动并测试七.注意事项八…...

中上211硕对嵌入式AI感兴趣,如何有效规划学习路径?

今天给大家分享的是一位粉丝的提问&#xff0c;中上211硕对嵌入式AI感兴趣&#xff0c;如何有效规划学习路径&#xff1f; 接下来把粉丝的具体提问和我的回复分享给大家&#xff0c;希望也能给一些类似情况的小伙伴一些启发和帮助。 同学提问&#xff1a; 中上211&#xff0c;…...

OpenCV中的边缘检测

边缘检测是图像处理和计算机视觉中的关键技术之一&#xff0c;旨在识别图像中像素强度发生显著变化的区域&#xff0c;这些区域通常对应于物体的边界或轮廓。边缘检测在机器视觉中具有重要的需求背景&#xff0c;主要体现在以下几个方面&#xff1a; 图像分割&#xff1a;边缘…...

Python爬虫-猫眼电影的影院数据

前言 本文是该专栏的第46篇,后面会持续分享python爬虫干货知识,记得关注。 本文笔者以猫眼电影为例子,获取猫眼的影院相关数据。 废话不多说,具体实现思路和详细逻辑,笔者将在正文结合完整代码进行详细介绍。接下来,跟着笔者直接往下看正文详细内容。(附带完整代码) …...