解决Promise的多并发问题
提起控制并发,大家应该不陌生,我们可以先来看看多并发,再去聊聊为什么要去控制它
多并发一般是指多个异步操作同时进行,而运行的环境中资源是有限的,短时间内过多的并发,会对所运行的环境造成很大的压力,比如前端的浏览器,后端的服务器,常见的多并发操作有:
- 前端的多个接口同时请求
- 前端多条数据异步处理
- Nodejs的多个数据操作同时进行
- Nodejs对多个文件同时进行修改

正是因为多并发会造成压力,所以我们才需要去控制他,降低这个压力~,比如我可以控制最大并发数是 3,这样的话即使有100个并发,我也能保证最多同时并发的最大数量是 3

代码实现
实现思路
大致思路就是,假设现在有 9 个并发,我设置最大并发为 3,那么我将会走下面这些步骤:
- 1、先定好三个坑位
- 2、让前三个并发进去坑位执行
- 3、看哪个坑位并发先执行完,就从剩余的并发中拿一个进去补坑
- 4、一直重复第 3 步,一直到所有并发执行完

Promise.all
在进行多并发的时候,我们通常会使用Promise.all,但是Promise.all并不能控制并发,或者说它本来就没这个能力,我们可以看下面的例子
const fetchFn = (delay, index) => {
return new Promise(resolve => {
console.log(index)
setTimeout(() => {
resolve(index)
}, delay);
})
}
const promises = [
fetchFn(1000, 1),
fetchFn(1000, 2),
fetchFn(1000, 3),
fetchFn(1000, 4),
fetchFn(1000, 5),
fetchFn(1000, 6)
]
Promise.all(promises)
最后是同时输出,这说明这几个并发是同时发生的

所以我们需要做一些改造,让Promise.all执行 promises 时支持控制并发,但是我们改造的不应该是Promise.all,而是这一个个的fetchFn
期望效果
const limitFn = (limit) => {
// ...coding
}
// 最大并发数 2
const generator = limitFn(2)
const promises = [
generator(() => fetchFn(1000, 1)),
generator(() => fetchFn(1000, 2)),
generator(() => fetchFn(1000, 3)),
generator(() => fetchFn(1000, 4)),
generator(() => fetchFn(1000, 5)),
generator(() => fetchFn(1000, 6))
]
Promise.all(promises)

实现 limitFn
我们需要在函数内部维护两个变量:
- queue:队列,用来存每一个改造过的并发
- activeCount:用来记录正在执行的并发数
并声明函数 generator ,这个函数返回一个 Promise,因为 Promise.all 最好是接收一个 Promise 数组
const limitFn = (concurrency) => {
const queue = [];
let activeCount = 0;
const generator = (fn, ...args) =>
new Promise((resolve) => {
enqueue(fn, resolve, ...args);
});
return generator;
};
接下来我们来实现 enqueue 这个函数做两件事:
- 将每一个 fetchFn 放进队列里
- 将坑位里的 fetchFn 先执行
const enqueue = (fn, resolve, ...args) => {
queue.push(run.bind(null, fn, resolve, ...args));
if (activeCount < limit && queue.length > 0) {
queue.shift()();
}
};
假如我设置最大并发数为 2,那么这一段代码在一开始的时候只会执行 2 次,因为一开始只会有 2 次符合 if 判断,大家可以思考一下为什么~
if (activeCount < limit && queue.length > 0) {
queue.shift()(); // 这段代码
}
一开始执行 2 次,说明这时候两个坑位已经各自有一个 fetchFn 在执行了
接下来我们实现 run 函数,这个函数是用来包装 fetch 的,他完成几件事情:
- 1、将 activeCount++ ,这时候执行中的并发数 +1
- 2、将 fetchFn 执行,并把结果 resolve 出去,说明这个并发执行完了
- 3、将 activeCount--,这时候执行中的并发数 -1
- 4、从 queue 中取一个并发,拿来补坑执行
const run = async (fn, resolve, ...args) => {
activeCount++;
const result = (async () => fn(...args))();
try {
const res = await result;
resolve(res);
} catch { }
next();
};
其实第 3、4 步,是在 next 函数里面执行的
const next = () => {
activeCount--;
if (queue.length > 0) {
queue.shift()();
}
};
完整代码
const limitFn = (limit) => {
const queue = [];
let activeCount = 0;
const next = () => {
activeCount--;
if (queue.length > 0) {
queue.shift()();
}
};
const run = async (fn, resolve, ...args) => {
activeCount++;
const result = (async () => fn(...args))();
try {
const res = await result;
resolve(res);
} catch { }
next();
};
const enqueue = (fn, resolve, ...args) => {
queue.push(run.bind(null, fn, resolve, ...args));
if (activeCount < limit && queue.length > 0) {
queue.shift()();
}
};
const generator = (fn, ...args) =>
new Promise((resolve) => {
enqueue(fn, resolve, ...args);
});
return generator;
};
这不是我写的
其实这是一个很出名的库的源码,就是p-limit,哈哈,但是重要吗?知识嘛,读懂了,它就是你的,到时跟面试官唠嗑的时候,他哪知道是不是真的是你写的~

解决Promise的多并发问题的更多相关文章
- nodejs 回调地狱解决 promise async
nodejs毁掉地狱是一直被人诟病的,以下总结一下解决毁掉地狱的一些方法.(暂时研究的比较浅) 1.promise promise模式在任何时刻都处于以下三种状态之一:未完成(unfulfilled) ...
- FluorineFx 播放FLV 时堆棧溢出解决 FluorineFx NetStream.play 并发时,无法全部连接成功的解决办法
http://25swf.blogbus.com/tag/FluorineFx/ http://www.doc88.com/p-7002019966618.html 基于Red5的视频监控系统的研究 ...
- php解决下单、抽奖并发导致的库存负数的问题
我们知道数据库处理sql是一条条处理的,假设购买商品的流程是这样的: sql1:查询商品库存 if(库存数量 > 0) { //生成订单... sql2:库存-1 } 当没有并发 ...
- 解决跨海高并发崩溃难题?so easy
近年来随着互联网强势的发展浪潮,越来越多的企业选择跨境出海,扩展海外市场.而想要在一个陌生市场最快速地吸引到用户,一定不能缺少的就是丰富多样的各类活动.然而活动在带来大流量的同时,也带来了一些问题,比 ...
- 解决秒杀活动高并发出现负库存(Redis)
商城在秒杀活动开始时,同时有好多人来请求这个接口,即便做了判断库存逻辑,也难免防止库存出现超卖,造成损失 Django中的ORM本身就对数据库做了防范,但再过亿级访问也扛不住 下面利用Redis的过载 ...
- 如何解决PHP的高并发和大流量的问题
基础知识 TFS : 吞吐量 (吞吐量是指系统在单位时间内处理请求的数量) RT : 响应时间 (从请求发出到收到响应时间) 并发数 : 在一段时间内同时访问站点的用户数 QPS : 每秒查询率 (每 ...
- 使用bluebird解决promise兼容性问题
//引入promiseif(!Promise){ var Promise = require("bluebird"); // Configure Promise.config({ ...
- double check 解决单例模式的多线程并发问题
最近被多线程问题(multi-thread issue)弄昏了头.以前虽然也知道系统里要考虑多线程问题,也无数次见到double-check的代码,但是由于自己碰到这方面的问题基本上就是从其他地方 ...
- ES7-ES12总结篇
脑图模式 插入 ES7-ES12 ES7 Array.prototype.includes() includes() 方法用来判断一个数组是否包含一个指定的值,如果包含则 ...
- asp.net解决高并发的方案.[转]
最近几天一直在读代震军的博客,他是Discuz!NT的设计者,读了他的一系列关于Discuz!NT的架构设计文章,大呼过瘾,特别是Discuz!NT在解决高访问高并发时所设计的一系列方案,本人尤其感兴 ...
随机推荐
- Hadoop的核心配置文件
1. core-site.xml:该配置文件包含了Hadoop通用配置,例如Hadoop的文件系统和I/O设置.Hadoop日志目录.Hadoop缓存设置等. 2. hdfs-site.xml:该配置 ...
- Hive常见时间日期函数的使用与问题整理
这里整理一下Hive常见的时间函数和日期函数和用法,作为平时数据处理过程的一个检索和记录. 平时在数据处理过程中,如果不经常使用时间函数,一时间遇到一些时间上的处理,难免会想不起来. hive本身提供 ...
- 流程挖掘里程碑:国产RPA首次入选顶级行业报告
正在成为组织运营标配的流程挖掘,到底有哪些商业价值? 作为超级自动化的重要先驱,流程挖掘正在成为组织运营标配 文/王吉伟 AIGC正在影响越来越多的行业,流程挖掘领域亦不例外. Mindzie首先宣布 ...
- 【Netty】01 - NIO
一. NIO 基础 non-blocking io 非阻塞 IO 1. 三大组件 1.1 Channel & Buffer channel 有一点类似于 stream,它就是读写数据的双向通道 ...
- 论文日记二:VGG
1. 导读 前面我们回顾了AlexNet,AlexNet的作者指出模型的深度很重要,而VGG最大的贡献就在于对网络模型深度的研究. VGG原论文:<Very Deep Convolutional ...
- Bellman-Ford算法及SPFA算法的思路及进一步优化
Bellman-Ford算法 算法 以边为研究对象的最短路算法. 应用场景 有负边权的最短路问题. 负环的判定. 算法原理 \(n\) 个点的最短路径最多经过 \(n - 1\) 条边. 每条边要么经 ...
- ZEGO即构科技荣获36氪【WISE2020中国新经济之王最具影响力企业】
12月8-10日,36氪重磅新经济峰会WISE2020新经济之王大会将在北京举办.近日,2020新经济之王--中国最具影响力企业榜单陆续发布,全球云通讯服务商即构科技,凭借在企业服务领域硬核出色的技术 ...
- 【技术积累】JavaScript中的基础语法【三】
JavaScript的条件结构 JavaScript中的条件结构主要包括if语句.if-else语句.if-else if语句和switch语句.这些条件结构用于根据不同的条件执行不同的代码块. if ...
- 利用InnoStep在VS编译时自动构建安装包
摘要 很多同学在C/S开发领域或多或少都可能会遇到需要制作安装包的场景,打包的工具也是五花八门,例如有NSIS.InstallShield.Wix Toolset.ClickOnce等等,这里以Inn ...
- java 线程等待和唤醒方法
java线程状态变迁图 从图中可以看出Java 线程等待方法是将线程从Runnable状态转换为Waiting状态,Java线程的唤醒方法是将线程从Waiting状态唤醒进入Runnable状态 在J ...