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

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

  • 前端的多个接口同时请求
  • 前端多条数据异步处理
  • 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. Framework 中使用 Toolkit.Mvvm 的生成器功能

    .NET Standard是.NET APIs的正式规范,可在多个.NET实现中使用..NET Standard的动机是为了在.NET生态系统中建立更大的统一性..NET 5及更高版本采用了不同的方法 ...

  2. 做副业的我很迷茫,但ChatGPT却治好了我——AI从业者被AI模型治愈的故事

    迷茫,无非就是不知道自己要做什么,没有目标,没有方向. 当有一个明确的目标时,往往干劲十足.但做副业过程中,最大的问题往往就是 不知道自己该干什么. 干什么?怎么干?干到什么程度?这是做副业(甚至任何 ...

  3. 之江实验室: 如何基于 JuiceFS 为超异构算力集群构建存储层 ?

    今天,高性能计算结合人工智能技术正在推动科研创新.例如通过破解水稻基因密码推动作物育种从"试验选优"向"计算选优"发展,在医药领域快速分析分子与蛋白之间的相互作 ...

  4. Python 引用问题 - ImportError: attempted relative import with no known parent package

    问题描述 近日在尝试引用其他文件的代码时,遇到了错误: ImportError: attempted relative import with no known parent package. 问题大 ...

  5. 【Python】万字长文,Locust 性能测试指北(上)

    Locust Locust 是比较常见的性能测试工具,底层基于 gevent.官方介绍 它是一款易于使用.可编写脚本且可扩展的性能测试工具,可以让我们使用常规 Python 代码定义用户的行为,而不必 ...

  6. The server time zone value '?泄???????' is unrecognized or represents more t

    hibernate配置文件如下 运行在服务器上,报错如下 解决方案: 在jdbc连接的url后面加上serverTimezone=GMT即可解决问题,因为是数据库和系统时区差异所造成的, 即 < ...

  7. jdbc-plus是一款基于JdbcTemplate增强工具包,基于JdbcTemplate已实现分页、多租户、动态表名等插件,可与mybatis、mybatis-plus等混合使用

    jdbc-plus简介 jdbc-plus是一款基于JdbcTemplate增强工具包,基于JdbcTemplate已实现分页.多租户.动态表名等插件,可与mybatis.mybatis-plus等混 ...

  8. grafana 容器无法启动,打印权限问题

    报错日志 open /var/lib/grafana/alerting/1/notifications: permission denied 问题原因 sudo chown -R docker: /v ...

  9. 好用工具: windows terminal

    直接在微软商店搜索该软件即可,本文介绍无法使用微软商店的情况. 解题思路 当我们无法下载某个软件时,可直接去Github中寻找该项目,知道该软件资源并下载. 下载地址 https://github.c ...

  10. CTF比赛中Web的php伪协议类型题小结

    php协议类型 file:// - 访问本地文件系统 http:// - 访问 HTTP(s) 网址 ftp:// - 访问 FTP(s) URLs php:// - 访问各个输入/输出流(I/O s ...