JavaScript/TypeScript 实现并发请求控制的示例代码

 更新时间:2021年01月18日 08:37:48   作者:凤晴铃玉  
这篇文章主要介绍了JavaScript/TypeScript 实现并发请求控制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

场景

假设有 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 并发请求控制内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Bootstrap每天必学之按钮(一)

    Bootstrap每天必学之按钮(一)

    Bootstrap每天必学之按钮,本文讲解的就是大家最为常用的button按钮的样式,感兴趣的小伙伴们可以参考一下
    2015-11-11
  • javascript程序优化问题

    javascript程序优化问题

    写了几年代码,很少谈到javascript程序的执行效率问题,今天就举几个例子看看,让大家看看程序优化是多么重要。 这节来看看createElement和innerHTML的表现。看看差别是多大
    2008-05-05
  • 深入学习JavaScript ES8中的函数式编程

    深入学习JavaScript ES8中的函数式编程

    函数式编程已经成为现代JavaScript开发中的一种主要范式,它提供了一种更清晰、更模块化、更可维护的代码编写方式,本文将深入探讨ES8中的一些关键特性,并演示如何使用这些特性进行函数式编程实践,有需要的可以参考下
    2023-09-09
  • 解决JSON.stringify()自动将中文转译成unicode的问题

    解决JSON.stringify()自动将中文转译成unicode的问题

    下面小编就为大家分享一篇解决JSON.stringify()自动将中文转译成unicode的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-01-01
  • js常用节点操作实例总结

    js常用节点操作实例总结

    这篇文章主要介绍了js常用节点操作,结合实例形式总结分析了JavaScript针对dom节点的遍历、查找、创建、删除、克隆等相关实现技巧与注意事项,需要的朋友可以参考下
    2023-04-04
  • 基于JavaScript实现自定义滚动条

    基于JavaScript实现自定义滚动条

    这篇文章主要为大家详细介绍了基于JavaScript实现自定义滚动条,可以直接使用的滚动条,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-01-01
  • JS实现浏览器状态栏文字从右向左弹出效果代码

    JS实现浏览器状态栏文字从右向左弹出效果代码

    这篇文章主要介绍了JS实现浏览器状态栏文字从右向左弹出效果,涉及JavaScript结合时间函数遍历字符串及动态改变状态栏显示效果的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-10-10
  • 屏蔽网页右键复制和ctrl+c复制的js代码

    屏蔽网页右键复制和ctrl+c复制的js代码

    解决的方法就是直接把网页保存下来然后删掉下面这段js代码,然后就可以正常用右键菜单,也可以通过设置浏览器的安全级别到最高级别来解决问题
    2013-01-01
  • 浅谈JavaScript中数组的增删改查

    浅谈JavaScript中数组的增删改查

    下面小编就为大家带来一篇浅谈JavaScript中数组的增删改查。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-06-06
  • 小程序显示弹窗时禁止下层的内容滚动实现方法

    小程序显示弹窗时禁止下层的内容滚动实现方法

    这篇文章主要介绍了小程序显示弹窗时禁止下层的内容滚动实现方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-03-03

最新评论