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

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

  • 前端的多个接口同时请求
  • 前端多条数据异步处理
  • 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. Win32 GUI 汇编

    获取句柄 API函数 GetModuleHandle 取模块句柄,lpModuleName 是一个指向模块名称字符串的指针,使用 NULL 获取当前程序句柄. invoke GetModuleHand ...

  2. JavaScript学习笔记 - 语法篇 - 一句废话没有版

    写在前面: 绝不废话!放心食用 JavaScript语法很简单,可以直接在控制台调试理解 目录 1.变量和常量 2.数据类型 3.字符串 3.1 模板字符串 3.2 字符串的部分常用函数 4.数组 5 ...

  3. 从JDK源码级深入剖析main方法的运行机制

    如果你是一名Java应用开发工程师,你应该对"public static void main(String[] args)"这段代码再熟悉不过了,然而你是否了解main方法是如何调 ...

  4. DevOps|中式土味OKR与绩效考核落地与实践

    昨天一个小伙伴和我讨论了一下OKR和绩效管理,所以这次想简单明了地说下在中国怎么做比较合适,很多高大上的理论无法落地也是空中楼阁. 首先说一些,我个人的理解 道德品质和能力素质决定了一个人的职位行为 ...

  5. TVM 源码阅读PASS — VectorizeLoop

    本文地址:https://www.cnblogs.com/wanger-sjtu/p/17501119.html VectorizeLoop这个PASS就是对标记为ForKind::kVectoriz ...

  6. DataX入门教学

    B站学习网址: https://www.bilibili.com/video/BV1H44y1x76X/?p=5&spm_id_from=pageDriver&vd_source=5f ...

  7. 计算机网络那些事之 MTU 篇

    哈喽大家好,我是咸鱼 今天我们来聊聊计算机网络中的 MTU (Maximum Transmission Unit) 什么是 MTU ? MTU(Maximum Transmission Unit)是指 ...

  8. 屏蔽CSDN百度广告

    最近在查询一些技术问题访问到CSDN时一直弹一些令人作恶的广告,说个特别的广告,脱发广告,特别有针对性程序员同胞们的共性问题,不过还是特别恶心,百度了一下,大家也特别反感,CSDN你真这么缺钱?废话不 ...

  9. 行行AI人才直播第8期:新加坡国立大学在读博士生张傲《多模态大语言模型(MLLM)的简介及高效训练》

    随着 ChatGPT 在各领域展现出非凡能力,多模态大型语言模型(MLLM)近来也成为了研究的热点,它利用强大的大型语言模型(LLM)作为"大脑",可以执行各种多模态任务.更让人感 ...

  10. 【websocket】小白快速上手flask-socketio

    大家好,我是一个初级的Python开发工程师.本文是结合官方教程和代码案例,简单说下我对flask-socketio的使用理解. 一.websocket简介 websocket 说白一点就是,建立客户 ...