提起控制并发,大家应该不陌生,我们可以先来看看多并发,再去聊聊为什么要去控制它

多并发一般是指多个异步操作同时进行,而运行的环境中资源是有限的,短时间内过多的并发,会对所运行的环境造成很大的压力,比如前端的浏览器,后端的服务器,常见的多并发操作有:

  • 前端的多个接口同时请求
  • 前端多条数据异步处理
  • 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的多并发问题的更多相关文章

  1. nodejs 回调地狱解决 promise async

    nodejs毁掉地狱是一直被人诟病的,以下总结一下解决毁掉地狱的一些方法.(暂时研究的比较浅) 1.promise promise模式在任何时刻都处于以下三种状态之一:未完成(unfulfilled) ...

  2. FluorineFx 播放FLV 时堆棧溢出解决 FluorineFx NetStream.play 并发时,无法全部连接成功的解决办法

    http://25swf.blogbus.com/tag/FluorineFx/ http://www.doc88.com/p-7002019966618.html  基于Red5的视频监控系统的研究 ...

  3. php解决下单、抽奖并发导致的库存负数的问题

    我们知道数据库处理sql是一条条处理的,假设购买商品的流程是这样的: sql1:查询商品库存 if(库存数量 > 0) {     //生成订单...     sql2:库存-1 } 当没有并发 ...

  4. 解决跨海高并发崩溃难题?so easy

    近年来随着互联网强势的发展浪潮,越来越多的企业选择跨境出海,扩展海外市场.而想要在一个陌生市场最快速地吸引到用户,一定不能缺少的就是丰富多样的各类活动.然而活动在带来大流量的同时,也带来了一些问题,比 ...

  5. 解决秒杀活动高并发出现负库存(Redis)

    商城在秒杀活动开始时,同时有好多人来请求这个接口,即便做了判断库存逻辑,也难免防止库存出现超卖,造成损失 Django中的ORM本身就对数据库做了防范,但再过亿级访问也扛不住 下面利用Redis的过载 ...

  6. 如何解决PHP的高并发和大流量的问题

    基础知识 TFS : 吞吐量 (吞吐量是指系统在单位时间内处理请求的数量) RT : 响应时间 (从请求发出到收到响应时间) 并发数 : 在一段时间内同时访问站点的用户数 QPS : 每秒查询率 (每 ...

  7. 使用bluebird解决promise兼容性问题

    //引入promiseif(!Promise){ var Promise = require("bluebird"); // Configure Promise.config({ ...

  8. double check 解决单例模式的多线程并发问题

      最近被多线程问题(multi-thread issue)弄昏了头.以前虽然也知道系统里要考虑多线程问题,也无数次见到double-check的代码,但是由于自己碰到这方面的问题基本上就是从其他地方 ...

  9. ES7-ES12总结篇

    脑图模式       插入 ES7-ES12  ES7  Array.prototype.includes()   includes() 方法用来判断一个数组是否包含一个指定的值,如果包含则 ...

  10. asp.net解决高并发的方案.[转]

    最近几天一直在读代震军的博客,他是Discuz!NT的设计者,读了他的一系列关于Discuz!NT的架构设计文章,大呼过瘾,特别是Discuz!NT在解决高访问高并发时所设计的一系列方案,本人尤其感兴 ...

随机推荐

  1. 使用 ChatGPT 的 7 个技巧 | Prompt Engineering 学习笔记

    概述 前段时间在 DeepLearning 学了一门大火的 Prompt 的课程,吴恩达本人授课,讲的通俗易懂,感觉受益匪浅,因此在这里总结分享一下我的学习笔记. 为什么要学习 Prompt ? 因为 ...

  2. 使用poi-tl导出word文件的几个技巧

    1.前言   Poi-tl提供了基于word模板文件导出word文件的功能.文档地址:http://deepoove.com/poi-tl/.   用下来,总体感觉还是很方便的.但使用过程,有几个细节 ...

  3. NSDI-2023 微软论文:解构有状态网络功能

    本文通过chatgpt代理站(支持gpt4):gptschools.cn翻译整理 微软Azure对每个虚拟机进行了为期三个月的网络监控,获得了新建.并发.PPS等指标情况,发现: 1) 网络功能负载不 ...

  4. FPGA加速技术在游戏和娱乐系统中的应用:实现高效的游戏和娱乐系统

    目录 1. 引言 2. 技术原理及概念 3. 实现步骤与流程 4. 应用示例与代码实现讲解 <35. FPGA加速技术在游戏和娱乐系统中的应用:实现高效的游戏和娱乐系统>这篇文章是一篇针对 ...

  5. React后台管理系统08 左侧菜单栏点击事件以及设置只有一个菜单展开项

    我们在Menu组件身上添加一个点击事件:对应的函数写一个回调函数:获取当前对象的e的身上的key, 这里其实不难看出e就是当前点击时的menu对象,我们这里获取的是e的key,对应上面定义的属性. 此 ...

  6. asp.net core如何获取客户端IP地址

    客户端直接访问服务器 直接通过HttpContext.Connection.RemoteIpAddress获取客户端Ip [HttpGet] [Route("GetClientIP" ...

  7. AI重塑千行百业,华为云发布盘古大模型3.0和昇腾AI云服务

    [中国,东莞,2023年7月7日]华为开发者大会2023(Cloud)7月7日在中国东莞正式揭开帷幕,并同时在全球10余个国家.中国30多个城市设有分会场,邀请全球开发者共聚一堂,就AI浪潮之下的产业 ...

  8. 快速实现 CDN 直播

    功能简介 ZEGO Express SDK 支持推流到 CDN(Content Delivery Network,内容分发网络),包括转推 CDN 和直推 CDN 两种功能.开发者基于该功能可打通 R ...

  9. 大语言模型中一个调皮的EOS token

    背景 最近需要做一个微调的培训,所以不可避免地需要上手一下相关的微调,而受限于机器资源,暂时没法做全参数微调,所以就尝试了目前比较火的两种高效微调方式,分别是PTuning和LoRA.模型选择得自然是 ...

  10. linux中利用crontab设置定时任务

    linux中利用crontab设置定时任务: # 每隔1个小时运行一次命令,四个*分别代表分,时,日,月,周 # crontab -l查看:crontab -e编辑 */1 * * * * /root ...