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 相关文章推荐
给页面渲染时间加速 干掉Dom Level 0 Event
Dec 19 Javascript
javascript中RegExp保留小数点后几位数的方法分享
Aug 13 Javascript
使用 js+正则表达式为关键词添加链接
Nov 11 Javascript
Jquery简单实现GridView行高亮的方法
Jun 15 Javascript
jQuery实现连续动画效果实例分析
Oct 09 Javascript
jQuery实现的指纹扫描效果实例(附演示与demo源码下载)
Jan 26 Javascript
浅谈JavaScript的内置对象和浏览器对象
Jun 03 Javascript
js removeChild 方法深入理解
Aug 16 Javascript
JS实现统计字符串中字符出现个数及最大个数功能示例
Jun 04 Javascript
JQuery实现简单的复选框树形结构图示例【附源码下载】
Jul 16 jQuery
vue-resource:jsonp请求百度搜索的接口示例
Nov 09 Javascript
JavaScript文档加载模式以及元素获取
Jul 28 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
php session应用实例 登录验证
2009/03/16 PHP
深入PHP许愿墙模块功能分析
2013/06/25 PHP
ThinkPHP验证码使用简明教程
2014/03/05 PHP
Linux系统递归生成目录中文件的md5的方法
2015/06/29 PHP
php封装的表单验证类完整实例
2016/10/19 PHP
利用PHP判断是手机移动端还是PC端访问的函数示例
2017/12/14 PHP
在JavaScript里嵌入大量字符串常量的实现方法
2013/07/07 Javascript
原生JS实现加入收藏夹的代码
2013/10/24 Javascript
JQuery验证jsp页面属性是否为空(实例代码)
2013/11/08 Javascript
javascript中直接引用Microsoft的COM生成Word
2014/01/20 Javascript
Javascript实现的常用算法(如冒泡、快速、鸽巢、奇偶等)
2014/04/29 Javascript
实现无刷新联动例子汇总
2015/05/20 Javascript
JavaScript数组复制详解
2017/02/02 Javascript
vue实现a标签点击高亮方法
2018/03/17 Javascript
chosen实现省市区三级联动
2018/08/16 Javascript
vue解决弹出蒙层滑动穿透问题的方法
2018/09/22 Javascript
js实现ATM机存取款功能
2020/10/27 Javascript
微信小程序监听用户登录事件的实现方法
2019/11/11 Javascript
Vue+Bootstrap收藏(点赞)功能逻辑与具体实现
2020/10/22 Javascript
python抓取京东价格分析京东商品价格走势
2014/01/09 Python
Python运行报错UnicodeDecodeError的解决方法
2016/06/07 Python
Pipenv一键搭建python虚拟环境的方法
2018/05/22 Python
转换科学计数法的数值字符串为decimal类型的方法
2018/07/16 Python
3个用于数据科学的顶级Python库
2018/09/29 Python
基于Python的图像数据增强Data Augmentation解析
2019/08/13 Python
详解python破解zip文件密码的方法
2020/01/13 Python
Python pandas如何向excel添加数据
2020/05/22 Python
H5 video poster属性设置视频封面的方法
2020/05/25 HTML / CSS
飞利浦西班牙官方网站:Philips西班牙
2020/02/17 全球购物
自我评价如何写好?
2014/01/05 职场文书
档案保密承诺书
2014/06/03 职场文书
本科毕业生应聘自荐信范文
2014/06/26 职场文书
2014年度考核工作总结
2014/12/24 职场文书
三八红旗手主要事迹材料
2015/11/04 职场文书
2016新教师岗前培训心得体会
2016/01/08 职场文书
《法国号》教学反思
2016/02/22 职场文书