JavaScript/TypeScript 实现并发请求控制的示例代码


Posted in Javascript onJanuary 18, 2021

场景

假设有 10 个请求,但是最大的并发数目是 5 个,并且要求拿到请求结果,这样就是一个简单的并发请求控制

模拟

利用 setTimeout 实行简单模仿一个请求

let startTime = Date.now();
const timeout = (timeout: number, ret: number) => {
 return (idx?: any) =>
 new Promise((resolve) => {
  setTimeout(() => {
  const compare = Date.now() - startTime;
  console.log(`At ${Math.floor(compare / 100)}00 return`, ret);
  resolve(idx);
  }, timeout);
 });
};

const timeout1 = timeout(1000, 1);
const timeout2 = timeout(300, 2);
const timeout3 = timeout(400, 3);
const timeout4 = timeout(500, 4);
const timeout5 = timeout(200, 5);

通过这样来模拟请求,本质就是 Promise

没有并发控制的时候

const run = async () => {
 startTime = Date.now();
 await Promise.all([
 timeout1(),
 timeout2(),
 timeout3(),
 timeout4(),
 timeout5(),
 ]);
};

run();

At 200 return 5
At 300 return 2
At 400 return 3
At 500 return 4
At 1000 return 1

可以看到输出是 5 2 3 4 1 ,按 timeout 的时间输出了

并发条件

假设同时间最大并发数目是 2,创建一个类

class Concurrent {
 private maxConcurrent: number = 2;

 constructor(count: number = 2) {
 this.maxConcurrent = count;
 }
}

第一种并发控制

想一下,按最大并发数拆分 Promise 数组,如果有 Promise 被 fulfilled 的时候,就移除掉,然后把 pending 状态的 Promise ,加进来。Promise.race 可以帮我们满足这个需求

class Concurrent {
 private maxConcurrent: number = 2;

 constructor(count: number = 2) {
 this.maxConcurrent = count;
 }
 public async useRace(fns: Function[]) {
 const runing: any[] = [];
 // 按并发数,把 Promise 加进去
 // Promise 会回调一个索引,方便我们知道哪个 Promise 已经 resolve 了
 for (let i = 0; i < this.maxConcurrent; i++) {
  if (fns.length) {
  const fn = fns.shift()!;
  runing.push(fn(i));
  }
 }
 const handle = async () => {
  if (fns.length) {
  const idx = await Promise.race<number>(runing);
  const nextFn = fns.shift()!;
  // 移除已经完成的 Promise,把新的进去
  runing.splice(idx, 1, nextFn(idx));
  handle();
  } else {
  // 如果数组已经被清空了,表面已经没有需要执行的 Promise 了,可以改成 Promise.all
  await Promise.all(runing);
  }
 };
 handle();
 }
}

const run = async () => {
 const concurrent = new Concurrent();
 startTime = Date.now();
 await concurrent.useRace([timeout1, timeout2, timeout3, timeout4, timeout5]);
};

At 300 return 2
At 700 return 3
At 1000 return 1
At 1200 return 5
At 1200 return 4

可以看到输出已经变了,为什么会这样呢,分析一下,最大并发数 2

// 首先执行的是 1 2
1 需要 1000 MS 才执行完
2 需要 300 MS

2 执行完,时间线变成 300 移除 2 加入 3 开始执行 3
3 需要 400MS 执行完时间变成 700 移除 3 加入 4 开始执行 4
4 需要 500MS
时间线来到 1000MS,1 执行完 移除 1 加入 5 开始执行 5
时间线来到 1200MS,4 和 5 刚好同时执行完

第二种方案

可以利用 await 的机制,其实也是一个小技巧

await 表达式会暂停当前 async function 的执行,等待 Promise 处理完成。若 Promise 正常处理(fulfilled),其回调的 resolve 函数参数作为 await 表达式的值,继续执行 async function。

如果当前的并发数已经超过最大的并发数目了,可以设置一个新的 Promise,并且 await,等待其他的请求完成的时候,resolve,移除等待,所以需要新增两个状态,当前的并发数目,还有用来存储 resolve 这个回调函数的数组

class Concurrent {
 private maxConcurrent: number = 2;
 private list: Function[] = [];
 private currentCount: number = 0;

 constructor(count: number = 2) {
 this.maxConcurrent = count;
 }
 public async add(fn: Function) {
 this.currentCount += 1;
 // 如果最大已经超过最大并发数
 if (this.currentCount > this.maxConcurrent) {
  // wait 是一个 Promise,只要调用 resolve 就会变成 fulfilled 状态
  const wait = new Promise((resolve) => {
  this.list.push(resolve);
  });
  // 在没有调用 resolve 的时候,这里会一直阻塞
  await wait;
 }
 // 执行函数
 await fn();
 this.currentCount -= 1;
 if (this.list.length) {
  // 把 resolve 拿出来,调用,这样 wait 就完成了,可以往下面执行了
  const resolveHandler = this.list.shift()!;
  resolveHandler();
 }
 }
}

const run = async () => {
 const concurrent = new Concurrent();
 startTime = Date.now();
 concurrent.add(timeout1);
 concurrent.add(timeout2);
 concurrent.add(timeout3);
 concurrent.add(timeout4);
 concurrent.add(timeout5);
};

run();

At 300 return 2
At 700 return 3
At 1000 return 1
At 1200 return 5
At 1200 return 4

总结

这两种方式都可以实现并发控制,只不过实现的方式不太一样,主要都是靠 Promise 实现,另外实现方式里面没有考虑异常的情况,这个可以自己加上

到此这篇关于JavaScript/TypeScript 实现并发请求控制的示例代码的文章就介绍到这了,更多相关JavaScript 并发请求控制内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
js中关于new Object时传参的一些细节分析
Mar 13 Javascript
IE中jquery.form中ajax提交没反应解决方法分享
Sep 11 Javascript
jQuery之选择组件的深入解析
Jun 19 Javascript
获取鼠标在div中的相对位置的实现代码
Dec 30 Javascript
js 采用delete实现继承示例代码
May 20 Javascript
JavaScript类型系统之正则表达式
Jan 05 Javascript
javascript日期比较方法实例分析
Jun 17 Javascript
详解vue-Resource(与后端数据交互)
Jan 16 Javascript
详解JavaScript中js对象与JSON格式字符串的相互转换
Feb 14 Javascript
JQuery 又谈ajax局部刷新
Nov 27 jQuery
VueJs使用Amaze ui调整列表和内容页面
Nov 30 Javascript
教你一步步实现一个简易promise
Nov 02 Javascript
js加减乘除精确运算方法实例代码
Jan 17 #Javascript
Angular处理未可知异常错误的方法详解
Jan 17 #Javascript
react-native 实现购物车滑动删除效果的示例代码
Jan 15 #Javascript
vue element el-transfer增加拖拽功能
Jan 15 #Vue.js
关于uniApp editor微信滑动问题
Jan 15 #Javascript
关于javascript中的promise的用法和注意事项(推荐)
Jan 15 #Javascript
详解node.js创建一个web服务器(Server)的详细步骤
Jan 15 #Javascript
You might like
php5数字型字符串加解密代码
2008/04/24 PHP
PHP发明人谈MVC和网站设计架构 貌似他不支持php用mvc
2011/06/04 PHP
PHP5中新增stdClass 内部保留类
2011/06/13 PHP
php UBB 解析实现代码
2011/11/27 PHP
php创建多级目录的方法
2015/03/24 PHP
PHP7 新特性详细介绍
2016/09/06 PHP
详谈PHP中public,private,protected,abstract等关键字的用法
2017/12/31 PHP
Prototype Function对象 学习
2009/07/12 Javascript
JS常见问题整理(持续更新)
2013/08/06 Javascript
JavaScript判断变量是否为undefined的两种写法区别
2013/12/04 Javascript
js实现双击图片放大单击缩小的方法
2015/02/17 Javascript
如何防止JavaScript自动插入分号
2015/11/05 Javascript
javascript html5移动端轻松实现文件上传
2020/03/27 Javascript
ajax在兼容模式下失效的快速解决方法
2016/03/22 Javascript
jQuery侧边栏实现代码
2016/05/06 Javascript
JS函数arguments数组获得实际传参数个数的实现方法
2016/05/28 Javascript
JavaScript实战之菜单特效
2016/08/16 Javascript
原生JS实现垂直手风琴效果
2017/02/19 Javascript
如何重置vue打印变量的显示方式
2017/12/06 Javascript
vue router 配置路由的方法
2018/07/26 Javascript
JS基于开关思想实现的数组去重功能【案例】
2019/02/18 Javascript
详解 微信小程序开发框架(MINA)
2019/05/17 Javascript
html+jQuery实现拖动滑块图片拼图验证码插件【移动端适用】
2019/09/10 jQuery
[56:14]Fnatic vs OG 2018国际邀请赛小组赛BO2 第二场 8.18
2018/08/19 DOTA
[08:47]2018国际邀请赛 OG战队举杯时刻
2018/08/29 DOTA
python简单读取大文件的方法
2016/07/01 Python
详解Python发送email的三种方式
2018/10/18 Python
在Python中调用Ping命令,批量IP的方法
2019/01/26 Python
python3实现zabbix告警推送钉钉的示例
2019/02/20 Python
python实现nao机器人手臂动作控制
2019/04/29 Python
使用C#编写创建一个线程的代码
2013/01/22 面试题
七年级数学教学反思
2014/01/22 职场文书
工厂车间标语
2014/06/19 职场文书
学生会生活部工作总结2015
2015/03/31 职场文书
钱学森电影观后感
2015/06/04 职场文书
css filter和getUserMedia的联合使用
2022/02/24 HTML / CSS