解决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在解决高访问高并发时所设计的一系列方案,本人尤其感兴 ...
随机推荐
- BFF层聚合查询服务异步改造及治理实践 | 京东云技术团队
首先感谢王晓老师的[接口优化的常见方案实战总结]一文总结,恰巧最近在对稳健理财BFF层聚合查询服务优化治理,针对文章内的串行改并行章节进行展开,分享下实践经验,主要涉及原同步改异步的过程.全异步化后衍 ...
- Spring Boot异步请求处理框架
Spring Boot异步请求处理框架 1.前言 在Spring Boot项目中,经常会遇到处理时间过长,导致出现HTTP请求超时问题,状态码:502. 例如一个文件导入接口需要导入一个Exc ...
- LeetCode 双周赛 106(2023/06/10)两道思维题
本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 加入知识星球提问. 往期回顾:LeetCode 单周赛第 348 场 · 数位 DP 模版学会了吗? 双周赛 106 ...
- 助力长城汽车数据管道平台连接“数据孤岛”,加强数据一元化,Apache DolphinScheduler 的角色定位
讲师简介 长城汽车-IDC-数据中台部-刘永飞 高级工程师 我是长城汽车 IDC-数据中台部的刘永飞,给大家分享一下我们自研的一个数据同步工具平台,以及在使用这个工具过程中遇到的问题.今天的分享主要有 ...
- Jenkins部署前后端不分离springboot项目
背景 写这篇博客的时候我还是大学生,学校期末课程设计时要求使用Jenkins部署项目,所以使用windows,但是企业中都是使用linux,往往还会搭建一个gitlab.下面我介绍的是在window环 ...
- 前端分页组件简单好用列表分页page组件
快速实现 简单好用列表分页组件, 分页器组件,用于展示页码.请求数据等 ,包含翻页. 详情请访问uni-app插件市场地址:https://ext.dcloud.net.cn/plugin?id=12 ...
- 6. Mybatis的各种查询功能
6.1.查询一个实体类对象 /** * 根据用户id查询用户信息 * @param id * @return */ User getUserById(@Param("id") ...
- 聊聊Excel解析:如何处理百万行EXCEL文件
一.引言 Excel表格在后台管理系统中使用非常广泛,多用来进行批量配置.数据导出工作.在日常开发中,我们也免不了进行Excel数据处理. 那么,如何恰当地处理数据量庞大的Excel文件,避免内存溢出 ...
- .NET写一个自己的Lambda表达式与表达式树
LambdaExpression继承Expression Expression又继承LambdaExpressio 所以,Expression与 Expression的区别在于:泛型类以静态类型的方法 ...
- 输入平方米的三种方式㎡ m2 m²
如何在Word中输入平方米字符? 第1种方法 Win10自带输入法,输入"平方米",默认第5个就出来了㎡,也可以直接复制使用. 这种方式最直接,字母m和右上角的2是1个字符,所以不 ...