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 相关文章推荐
jQuery基于cookie实现的购物车实例分析
Dec 24 Javascript
js计算时间差代码【包括计算,天,时,分,秒】
Apr 26 Javascript
AngularJS 视图详解及示例代码
Aug 17 Javascript
JavaScript实现经典排序算法之冒泡排序
Dec 28 Javascript
js a标签点击事件
Mar 30 Javascript
jQuery实现文章图片弹出放大效果
Apr 06 jQuery
在node中如何使用 ES6
Apr 22 Javascript
mui 打开新窗口的方式总结及注意事项
Aug 20 Javascript
JS中利用swiper实现3d翻转幻灯片实例代码
Aug 25 Javascript
JS如何获取地址栏的参数实例讲解
Oct 06 Javascript
vue实现密码显示与隐藏按钮的自定义组件功能
Apr 23 Javascript
vue.js watch经常失效的场景与解决方案
Jan 07 Vue.js
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/07/29 PHP
php中require和require_once的区别说明
2014/02/27 PHP
thinkPHP实现递归循环栏目并按照树形结构无限极输出的方法
2016/05/19 PHP
Laravel5.3+框架定义API路径取消CSRF保护方法详解
2020/04/06 PHP
写的htc的数据表格
2007/01/20 Javascript
jquery uaMatch源代码
2011/02/14 Javascript
javascript学习笔记(八)正则表达式
2014/10/08 Javascript
jQuery实现自动调用和触发某个事件的方法
2016/11/18 Javascript
前端分页功能的实现以及原理(jQuery)
2017/01/22 Javascript
jQuery插件FusionCharts实现的3D帕累托图效果示例【附demo源码】
2017/03/25 jQuery
JavaScript canvas实现围绕旋转动画
2017/11/18 Javascript
实例讲解Vue.js中router传参
2018/04/22 Javascript
layui 给数据表格加序号的方法
2018/08/20 Javascript
vuejs 制作背景淡入淡出切换动画的实例
2018/09/01 Javascript
微信小程序使用蓝牙小插件
2019/09/23 Javascript
jQuery使用ajax传递json对象到服务端及contentType的用法示例
2020/03/12 jQuery
[03:48]2014DOTA2 TI专访71DK夺冠不靠小组赛高排名
2014/07/11 DOTA
[02:05:03]完美世界DOTA2联赛循环赛 LBZS VS Matador BO2 10.28
2020/10/28 DOTA
python类继承与子类实例初始化用法分析
2015/04/17 Python
基于Django filter中用contains和icontains的区别(详解)
2017/12/12 Python
python绘制双柱形图代码实例
2017/12/14 Python
Python爬虫实现百度图片自动下载
2018/02/04 Python
查找python项目依赖并生成requirements.txt的方法
2018/07/10 Python
Python HTML解析器BeautifulSoup用法实例详解【爬虫解析器】
2019/04/05 Python
谈一谈基于python的面向对象编程基础
2019/05/21 Python
美国领先的在线邮轮旅游公司:CruiseDirect
2018/06/07 全球购物
Java如何获得ResultSet的总行数
2016/09/03 面试题
商务日语毕业生自荐信范文
2013/11/14 职场文书
团日活动总结书格式
2014/05/08 职场文书
领导班子整改措施
2014/10/24 职场文书
2014年标准化工作总结
2014/12/17 职场文书
鸡毛信观后感
2015/06/11 职场文书
《伯牙绝弦》教学反思
2016/02/16 职场文书
iPhone13将有八大升级
2021/04/15 数码科技
Unity连接MySQL并读取表格数据的实现代码
2021/06/20 MySQL
什么是clearfix (一文搞清楚css清除浮动clearfix)
2023/05/21 HTML / CSS