js promise如何控制异步并发操作量?

news/2025/3/22 18:13:58/文章来源:https://www.cnblogs.com/soarowl/p/18786921

在 JavaScript 中控制异步并发操作量(即限制同时执行的异步任务数量)是优化性能和资源管理的关键。以下是多种实现方法及其详细说明:


一、使用 Promise 和队列控制并发

通过自定义队列系统限制并发数,逐步执行任务。

代码实现

async function asyncPool(poolLimit, tasks) {const executing = new Set(); // 记录正在执行的任务const results = [];for (const task of tasks) {// 如果当前并发数已达上限,等待任意一个任务完成if (executing.size >= poolLimit) {await Promise.race(executing);}const p = task().then(result => {executing.delete(p); // 任务完成后从执行队列移除results.push(result);return result;});executing.add(p); // 将任务加入执行队列}// 等待所有剩余任务完成await Promise.all(executing);return results;
}// 使用示例
const tasks = [() => fetch('url1'),() => fetch('url2'),// ...更多任务
];
asyncPool(3, tasks).then(results => {console.log('所有任务完成:', results);
});

核心逻辑

  1. 使用 Set 记录正在执行的任务。
  2. 当并发数超过限制时,通过 Promise.race() 等待任意一个任务完成。
  3. 任务完成后更新队列并记录结果。

二、第三方库 p-limit

使用轻量级库 p-limit 快速实现并发控制。

安装与使用

npm install p-limit
import pLimit from 'p-limit';const limit = pLimit(3); // 最大并发数 3const tasks = [limit(() => fetch('url1')),limit(() => fetch('url2')),// ...更多任务
];// 等待所有任务完成
Promise.all(tasks).then(results => {console.log(results);
});

优点

  • 代码简洁,无需手动管理队列。
  • 支持动态添加任务。

三、分批次执行(Batch Processing)

将任务拆分成多个批次,逐批执行。

代码实现

async function batchProcess(tasks, batchSize) {const results = [];for (let i = 0; i < tasks.length; i += batchSize) {const batch = tasks.slice(i, i + batchSize);const batchResults = await Promise.all(batch.map(task => task()));results.push(...batchResults);}return results;
}// 使用示例
const tasks = [() => fetch('url1'),() => fetch('url2'),// ...更多任务
];
batchProcess(tasks, 3).then(results => {console.log('所有批次完成:', results);
});

缺点

  • 前一批次全部完成后才会执行下一批次,资源利用率较低。

四、基于 Promise 的手动调度

通过递归或循环动态启动任务。

代码实现

function controlledConcurrency(tasks, concurrency) {let index = 0;const results = [];// 递归执行任务const runTask = async () => {if (index >= tasks.length) return;const taskIndex = index++;const task = tasks[taskIndex];try {results[taskIndex] = await task();} catch (error) {results[taskIndex] = error;}await runTask(); // 继续执行下一个任务};// 启动初始的并发任务const workers = Array.from({ length: Math.min(concurrency, tasks.length) },() => runTask());return Promise.all(workers).then(() => results);
}// 使用示例
controlledConcurrency(tasks, 3).then(results => {console.log('所有任务完成:', results);
});

五、使用 async-pool

专为并发控制设计的库,支持灵活配置。

安装与使用

npm install async-pool
import asyncPool from 'async-pool';const timeout = (ms) => new Promise(resolve => setTimeout(resolve, ms));async function run() {const tasks = Array.from({ length: 10 }, (_, i) => () => timeout(1000).then(() => i));const results = await asyncPool(3, tasks);console.log(results); // [0, 1, 2, 3, ...]
}run();

六、浏览器环境中的 Promise.withResolvers()(ES2024+)

利用最新的 JavaScript 特性简化代码。

async function controlledConcurrency(tasks, concurrency) {const results = [];const queue = [...tasks];const workers = Array.from({ length: concurrency }, async () => {while (queue.length) {const task = queue.shift();const { promise, resolve, reject } = Promise.withResolvers();try {const result = await task();results.push(result);resolve(result);} catch (error) {reject(error);}await promise;}});await Promise.all(workers);return results;
}

总结

方法 适用场景 优点 缺点
手动队列控制 需要精细控制逻辑 灵活,无依赖 代码复杂度高
p-limit 快速实现并发控制 简洁,社区维护 需安装第三方库
分批次执行 简单分批任务 实现简单 资源利用率低
手动调度 动态任务管理 灵活控制任务启动顺序 需处理递归逻辑
async-pool 需要现成解决方案 功能丰富 需安装第三方库

根据需求选择:

  • 简单场景:使用 p-limit 或分批次执行。
  • 精细控制:手动队列或递归调度。
  • 最新特性:ES2024 的 Promise.withResolvers()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/903555.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

关于 LCA (最近公共祖先)

最近公共祖先!对于一棵树上的两个点,他们的所有公共祖先中深度最大的那一个被称为它们的最近公共祖先(LCA)。求 LCA 有很多不同的方法。倍增 倍增求 LCA,首先需要对树进行 dfs(废话),标记每个节点的直接父亲(\(2^0\) 级祖先)。然后我们就可以利用倍增的思想预处理它的…

初入博客园

Start 第一篇随笔记录一下对于博客园的便利使用技巧。 自定义皮肤 安装和配置博客园皮肤全称按照了guangzan大佬的说明教程。[1] 里面附带有一有获取音乐链接以及歌词的网站,感觉很有用😍;[2] 贴一个网站头部背景图(苹果初代机-麦金塔Macintosh):待更新 🍀🍀🍀htt…

PCIe总线-RK3588 PCIe平台驱动分析

1.简介 RK3588 PCIe RC和EP使用同一个平台驱动,其主要的作用是解析设备树中的资源、初始化中断、使能电源、初始化PHY、使能时钟和释放复位,然后根据compatible属性初始化RC或者EP驱动。 2.入口 平台驱动的定义如下,当compatible属性为"rockchip,rk3588-pcie",则…

202413350081刁嘉怡博客园2

TASK11 #include <stdio.h>2 #include <stdlib.h>3 #include <time.h>4 5 #define N 56 7 int main() {8 int number;9 int i; 10 11 srand(time(0)); // 以当前系统时间作为随机种子 12 for(i = 0; i < N; ++i) { 13 numbe…

四则运算结对项目

这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/SoftwareEngineeringClassof2023这个作业的目标 结对合作生成一个四则运算题目生成器结对成员 3123004365彭颂华 &&3123004354黄子恒github地址 https://github.com/PShua/zuoye &&https://github.c…

Vue3 vite 集成 sass

一、安装依赖npm install sass-embedded二、配置全局变量 1、新建文件 src/styles/variables.scss 2、配置Vite 修改 vite.config.jscss: {preprocessorOptions: {scss: {additionalData: `@use "@/styles/variables" as *;`,},},},3、测试使用 a、在variables.scss…

在Android studio 里面想要写入文本内容怎么做

在 AndroidManifest.xml 文件中添加写入外部存储的权限。如果是 Android 6.0(API 级别 23)及以上的系统,还需要在运行时请求权限。在 MainActivity 的 writeToFile 方法里调用 FileHelper 类: private void writeToFile(String content) { FileHelper.writeToInternalStora…

iptables 介绍与实战

iptables是Linux内核中用于配置防火墙规则的工具。它基于Netfilter框架,可以对通过网络接口的数据包进行过滤、修改等操作。通过设置一系列规则,iptables能够控制哪些数据包可以进入或离开系统,从而实现网络安全防护等功能。它主要工作在网络层,能够根据数据包的源地址、目…

CF771E题解

CF771E题解很容易设出 \(dp_{i,j}\) 表示第一行选到 \(i\),第二行选到 \(j\) 的方案数 首先考虑部分分。 \(|a_i|\le 1\),那么产生贡献的一个矩阵不会超过 \(2\),那么就没必要考虑 \(|i-j|\ge 4\) 的状态了。证明如下: 不妨设 \(i<j\),那么我与其从 \(dp_{i,j}\to dp_{…

Word目录链接中只选中开头的文字

Word目录链接中只选中开头的文字 Word目录想选第一个字却选中整行 word自动生成目录里选中第一个字不是选整段就是选一行,怎么解决解决方法: 【不行】按上档键Shift+Home键。 【不行】Windows有个很强大的Alt健,按住Alt之后,在目录里面就可以想选哪个选哪个,不会出现再你这…

基于MPPT控制的锂离子电池充电器

基于MPPT的集成电路,可跟踪电源点并对电池充电,最大额定功率为1A,外形尺寸为TP4056。当谈到独立的单电池充电器时,只有一个流行的名字进入我的脑海,那就是我们的多功能锂离子/锂po TP4056电池充电器。这是广泛使用的,有很多功能与电池保护有关。锂电池在市场上很容易获得…

【Linux文件】把/etc/passwd删除了,该怎么办?

场景: 在做渗透测试发现网站存在任意文件删除漏洞,测试删除了/etc/passwd,那么删除后该如何恢复?一、 /etc/passwd文件的作用 /etc/passwd 是 Linux 系统中存储用户账户信息的关键文件,包含用户名、UID、GID、主目录路径和默认 Shell 等。删除 /etc/passwd 仍会导致以下问题…