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实现的页内搜索代码
May 23 Javascript
javascript 主动派发事件总结
Aug 09 Javascript
基于jQuery的简单九宫格实现代码
Aug 09 Javascript
js计算两个时间之间天数差的实例代码
Nov 19 Javascript
用C/C++来实现 Node.js 的模块(二)
Sep 24 Javascript
JavaScript实现16进制颜色值转RGB的方法
Feb 09 Javascript
基于jQuery实现拖拽图标到回收站并删除功能
Nov 25 Javascript
JS使用Dijkstra算法求解最短路径
Jan 17 Javascript
koa2 从入门到精通(小结)
Jul 23 Javascript
js常用方法、检查是否有特殊字符串、倒序截取字符串操作完整示例
Jan 26 Javascript
js实现上传图片到服务器
Apr 11 Javascript
Vue实现跑马灯样式文字横向滚动
Nov 23 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
最令PHP初学者头痛的十四个问题
2006/07/12 PHP
检查php文件中是否含有bom的函数
2012/05/31 PHP
PHP获取一年中每个星期的开始和结束日期的方法
2015/02/12 PHP
浅谈使用PHP开发微信支付的流程
2015/10/04 PHP
php  PATH_SEPARATOR判断当前服务器系统类型实例
2016/10/28 PHP
javascript下操作css的float属性的特殊写法
2007/08/22 Javascript
jQuery当鼠标悬停时放大图片的效果实例
2013/07/03 Javascript
iframe调用父页面函数示例详解
2014/07/17 Javascript
javascript验证身份证号
2015/03/03 Javascript
javascript顺序加载图片的方法
2015/07/18 Javascript
ES6新特性之解构、参数、模块和记号用法示例
2017/04/01 Javascript
jQuery Datatable 多个查询条件自定义提交事件(推荐)
2017/08/24 jQuery
Nuxt.js实战详解
2018/01/18 Javascript
[原创]js实现保存文本框内容为本地文件兼容IE,chrome,火狐浏览器
2018/02/14 Javascript
react中实现搜索结果中关键词高亮显示
2018/07/31 Javascript
JS实现盒子跟着鼠标移动及键盘方向键控制盒子移动效果示例
2019/01/29 Javascript
jquery ajax 请求小技巧实例分析
2019/11/11 jQuery
小程序登录之支付宝授权的实现示例
2019/12/13 Javascript
vue 判断元素内容是否超过宽度的方式
2020/07/29 Javascript
javascript实现简单页面倒计时
2021/03/02 Javascript
Linux下使用python调用top命令获得CPU利用率
2015/03/10 Python
python实现合并两个数组的方法
2015/05/16 Python
Python脚本暴力破解栅栏密码
2015/10/19 Python
Python闭包的两个注意事项(推荐)
2017/03/20 Python
Python中模块pymysql查询结果后如何获取字段列表
2017/06/05 Python
通过python的matplotlib包将Tensorflow数据进行可视化的方法
2019/01/09 Python
python使用turtle绘制国际象棋棋盘
2019/05/23 Python
python 设置xlabel,ylabel 坐标轴字体大小,字体类型
2019/07/23 Python
python爬虫数据保存到mongoDB的实例方法
2020/07/28 Python
CSS3实现线性渐变用法示例代码详解
2020/08/07 HTML / CSS
J2EE相关知识面试题
2013/08/26 面试题
《苏珊的帽子》教学反思
2014/04/07 职场文书
学习党的群众路线教育实践活动心得体会范文
2014/11/03 职场文书
毕业实习证明范本
2015/06/16 职场文书
Nginx配置并兼容HTTP实现代码解析
2021/03/31 Servers
浅谈Redis跟MySQL的双写问题解决方案
2022/02/24 Redis