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 相关文章推荐
ExtJS Window 最小化的一种方法
Nov 18 Javascript
js中将具有数字属性名的对象转换为数组
Mar 06 Javascript
JavaScript中json对象和string对象之间相互转化
Dec 26 Javascript
js实现单一html页面两套css切换代码
Apr 11 Javascript
深入理解JavaScript高级之词法作用域和作用域链
Dec 10 Javascript
详解参数传递四种形式
Jul 21 Javascript
JS 中使用Promise 实现红绿灯实例代码(demo)
Oct 20 Javascript
JSON数据中存在单个转义字符“\”的处理方法
Jul 11 Javascript
JS实现的小火箭发射动画效果示例
Dec 08 Javascript
微信小程序人脸识别功能代码实例
May 07 Javascript
JavaScript实现随机点名器
Mar 25 Javascript
easyUI使用分页过滤器对数据进行分页操作实例分析
Jun 01 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 求质素(素数) 的实现代码
2011/04/12 PHP
PHP实现获取中英文首字母
2015/06/19 PHP
php htmlentities()函数的定义和用法
2016/05/13 PHP
thinkPHP5框架设置404、403等http状态页面的方法
2018/06/05 PHP
PHP 扩展Memcached命令用法实例总结
2020/06/04 PHP
ExtJs使用IFrame的实现代码
2010/03/24 Javascript
JS request函数 用来获取url参数
2010/05/17 Javascript
为开发者准备的10款最好的jQuery日历插件
2014/02/04 Javascript
javascript基本类型详解
2014/11/28 Javascript
JavaScript类继承及实例化的方法
2015/07/25 Javascript
JavaScript开发者必备的10个Sublime Text插件
2016/02/27 Javascript
jQuery自适应轮播图插件Swiper用法示例
2016/08/24 Javascript
yii form 表单提交之前JS在提交按钮的验证方法
2017/03/15 Javascript
微信小程序实现倒计时60s获取验证码
2020/04/17 Javascript
微信小程序授权获取用户详细信息openid的实例详解
2017/09/20 Javascript
Vue登录注册并保持登录状态的方法
2018/08/17 Javascript
VUE 实现复制内容到剪贴板的两种方法
2019/04/24 Javascript
vue中根据时间戳判断对应的时间(今天 昨天 前天)
2019/12/20 Javascript
vue路由权限校验功能的实现代码
2020/06/07 Javascript
详解Vue数据驱动原理
2020/11/17 Javascript
Python中使用Inotify监控文件实例
2015/02/14 Python
python在windows和linux下获得本机本地ip地址方法小结
2015/03/20 Python
详解Django中的ifequal和ifnotequal标签使用
2015/07/16 Python
python爬虫基本知识
2018/03/05 Python
python中logging模块的一些简单用法的使用
2019/02/22 Python
使用Python生成200个激活码的实现方法
2019/11/22 Python
匡威爱尔兰官网:Converse爱尔兰
2019/06/09 全球购物
高中生学习生活的自我评价
2013/10/09 职场文书
医学生求职信
2014/07/01 职场文书
民事辩护词范文
2015/05/21 职场文书
个人欠条范本
2015/07/03 职场文书
优质服务标语口号
2015/12/26 职场文书
MySQL 慢查询日志深入理解
2021/04/22 MySQL
Python标准库之typing的用法(类型标注)
2021/06/02 Python
手写实现JS中的new
2021/11/07 Javascript
Ajax异步刷新功能及简单案例
2021/11/20 Javascript