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 相关文章推荐
给jqGrid数据行添加修改和删除操作链接(之一)
Nov 04 Javascript
jquery $.getJSON()跨域请求
Dec 21 Javascript
jQuery DOM插入节点操作指南
Mar 03 Javascript
JavaScript箭头函数_动力节点Java学院整理
Jun 28 Javascript
使用html+js+css 实现页面轮播图效果(实例讲解)
Sep 21 Javascript
基于vue 开发中出现警告问题去除方法
Jan 25 Javascript
详解swiper在vue中的应用(以3.0为例)
Sep 20 Javascript
jQuery pagination分页示例详解
Oct 23 jQuery
微信内置开发 iOS修改键盘换行为搜索的解决方案
Nov 06 Javascript
微信小程序中限制激励式视频广告位显示次数(实现思路)
Dec 06 Javascript
功能完善的小程序日历组件的实现
Mar 31 Javascript
vue实现商品列表的添加删除实例讲解
May 14 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
模仿OSO的论坛(四)
2006/10/09 PHP
php设计模式 Template (模板模式)
2011/06/26 PHP
判断PHP数组是否为空的代码
2011/09/08 PHP
php和editplus正则表达式去除空白行
2015/04/17 PHP
一个非常实用的php文件上传类
2017/07/04 PHP
PDO::_construct讲解
2019/01/27 PHP
jquery 简单导航实现代码
2009/09/11 Javascript
JavaScript中的View-Model使用介绍
2011/08/11 Javascript
点击弹出层效果&amp;弹出窗口后网页背景变暗效果的实现代码
2014/02/10 Javascript
jquery通过扩展select控件实现支持enter或focus选择的方法
2015/11/19 Javascript
最佳的JavaScript错误处理实践
2016/07/16 Javascript
Javascript实现时间倒计时效果
2017/07/15 Javascript
JS获取指定月份的天数两种实现方法
2018/06/22 Javascript
JavaScript多种图形实现代码实例
2020/06/28 Javascript
[39:00]Optic vs VP 2018国际邀请赛淘汰赛BO3 第三场 8.24
2018/08/25 DOTA
通过Python爬虫代理IP快速增加博客阅读量
2016/12/14 Python
python使用PIL模块获取图片像素点的方法
2019/01/08 Python
Python用Try语句捕获异常的实例方法
2019/06/26 Python
Django admin.py 在修改/添加表单界面显示额外字段的方法
2019/08/22 Python
python3.7环境下安装Anaconda的教程图解
2019/09/10 Python
使用Fabric自动化部署Django项目的实现
2019/09/27 Python
python实现PCA降维的示例详解
2020/02/24 Python
Python3自定义http/https请求拦截mitmproxy脚本实例
2020/05/11 Python
浅谈Selenium 控制浏览器的常用方法
2020/12/04 Python
美国知名保健品网站:LuckyVitamin(支持中文)
2017/08/09 全球购物
Laura Geller官网:美国彩妆品牌
2018/12/29 全球购物
欧克利英国官网:Oakley英国
2019/08/24 全球购物
新西兰最大的连锁超市:Countdown
2020/06/04 全球购物
J2EE面试题
2016/03/14 面试题
优秀毕业大学生推荐信
2013/11/13 职场文书
镇人大副主席民主生活会对照检查材料思想汇报
2014/10/01 职场文书
工会2014法制宣传日活动总结
2014/11/01 职场文书
赔偿协议书怎么写
2015/01/28 职场文书
保送生自荐信
2015/03/06 职场文书
优秀创业计划书分享
2019/07/19 职场文书
如何利用python和DOS获取wifi密码
2021/03/31 Python