首页 > 其他分享 >记录--前端实现并发请求限制

记录--前端实现并发请求限制

时间:2023-09-26 18:22:05浏览次数:42  
标签:item -- 前端 并发 running Promise asyncLimit asyncExecute

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

前言

前两天我的新同事告诉我一个困扰着他的问题,就是低代码平台中存在很多模块,这些模块的渲染是由模块自身处理的,简言之就是组件请求了自己的数据,一个两个模块还好,要是一次请求了几十个模块,就会出现请求阻塞的问题,而且模块的请求都特别大。

大量的并发请求会导致网络拥塞和带宽限制。特别是当网络带宽有限时,同时发送大量请求可能会导致请求之间的竞争,从而导致请求的响应时间延长。

因此模块的加载就很不顺畅。。。

为了解决这个问题我设计了一个关于前端实现并发请求限制的方案,下面将详细解释这个并发请求限制的方案及实现源码。

核心思路及简易实现

一、收集需要并发的接口列表,并发数,接口调用函数。

二、遍历arr,对于每一项,创建一个promise实例存储到resArr中,创建的时候就已经开始执行了。

三、将创建的promise传入的实例数组中,对于每一项的promise设置其then操作,并将其存储到running数组中,作为执行中的标识。

四、当then操作触发之后则将running中的对应这一项删除,执行中的数组减一。

五、在遍历的回调函数最后判断当前是否超出阈值,当数量达到限制时开始批量执行,用await去处理异步,处理完一个即跳走,重新往running中注入新的实例。

async function asyncLimit(limitNum, arr, fn) {
  let resArr = []; // 所有promise实例
  let running = []; // 执行中的promise数组
  for (const item of arr) {
    const p = Promise.resolve(fn(item)); // 遍历arr,对于每一项,创建一个promise实例存储到resArr中,创建的时候就已经开始执行了
    resArr.push(p);
    if (arr.length >= limitNum) {
      // 对于每一项设置其then操作,并将其存储到running数组中,作为执行中的标识,当then操作触发之后则将running中的对应这一项删除,执行中的数组减一
      const e = p.then(() => running.splice(running.indexOf(e), 1));
      running.push(e);
      if (running.length >= limitNum) {
        // 当数量达到限制时开始批量执行,处理完一个即跳走,重新往running中注入新的实例
        await Promise.race(running);
      }
    }
  }
  return Promise.allSettled(resArr);
}
用例:
fn = (item) => { 
    return new Promise((resolve) => { 
        console.log("开始",item); 
        setTimeout(() => { console.log("结束", item); 
        resolve(item); 
     }, item) }); 
}; 
asyncLimit(2, [1000, 2000, 5000, 2000, 3000], fn)

注:但是这里的实现太过简陋,在真正的业务场景中往往没有这样使用场景,因此我对着段代码封装成一个符合前端使用的并发限制模块,下面是完整可用的代码实现

完整实现及源码用例

首先,让我们来看一下代码及用例:

let targetArray = []; // 目标调用数组
let resultArray = []; // 结果数组
let runningPromise = null; // 正在运行的 Promise
let limitNum = 0; // 最大并发数
let defaultFn = (value) => value; // 默认处理函数

 function asyncInit(limit, fn) {
  limitNum = limit;
  defaultFn = fn;
}

 async function asyncLimit(arr) {
  const promiseArray = []; // 所有 Promise 实例
  const running = []; // 正在执行的 Promise 数组

  for (const item of arr) {
    const p = Promise.resolve((item.fn || defaultFn)(item.value || item)); // 调用元素的处理函数
    promiseArray.push(p);

    if (arr.length >= limitNum) {
      const e = p.then(() => running.splice(running.indexOf(e), 1));
      running.push(e);

      if (running.length >= limitNum) {
        await Promise.race(running);
      }
    }
  }

  return Promise.allSettled(promiseArray);
}

 function asyncExecute(item) {
  targetArray.push(item);

  if (!runningPromise) {
    runningPromise = Promise.resolve().then(()=>{
      asyncLimit(targetArray).then((res) => {
        resultArray.push(...res);
        targetArray = [];
        runningPromise = null;
      });
    })
  }
}

这里提供了一个并发模块的文件。

简单用例:

asyncInit(3, (item) => {
  return new Promise((resolve) => {
    console.log("开始",item);
    setTimeout(() => {
      console.log("结束", item);
      resolve(item);
    }, item)
  });
})

asyncExecute({value: 1000})
asyncExecute({value: 2000})
asyncExecute({value: 5000})
asyncExecute({value: 2000})
asyncExecute({value: 3000})

效果:

注:可以看到我们在使用的时候只需要先初始化最大并发数和默认调用函数,即可直接调用asyncExecute去触发并发请求而且通过源码我们可以看到如果 asyncExecute 的参数可以自定义调用函数,及传入的对象中包含fn即可。

重点: 因为这些内容都被抽离成一个文件,所以我们可以导出asyncExecute这个函数然后业务侧不同位置都可以通过这个函数去发起请求,这样就能实现项目中所有请求的并发限制。

代码解释

这段代码实现了一个前端并发限制的机制。让我们逐步解释其中的关键部分。

第一步

我们定义了一些变量,包括目标调用数组 targetArray 、结果数组 resultArray 、正在运行的 Promise runningPromise 、最大并发数 limitNum 和默认处理函数 defaultFn

第二步

定义 asyncInit 函数,用于初始化并发限制的最大数和默认处理函数。通过调用该函数,我们可以设置相关并发限制的参数。

第三步

然后,我们定义了 asyncLimit 函数,用于实现并发限制的核心逻辑。

在这个函数中,我们遍历传入的数组 arr ,对每个元素执行处理函数,并将返回的 Promise 实例存储在 promiseArray 数组中。

同时,我们使用 running 数组来跟踪正在执行的 Promise 实例。

如果当前正在执行的 Promise 实例数量达到最大并发数,我们使用 Promise.race 方法等待最先完成的 Promise,以确保并发数始终保持在限制范围内。(这里对应的就是核心思路及简易实现中的代码)

注:如果要实现异步并发,我们只要保证我们的接口存在于传入的数组即arr中即可。

第四步

定义 asyncExecute 函数,用于触发异步操作的执行。

当调用 asyncExecute 函数时,我们将目标元素添加到 targetArray 数组中,这个targetArray就是异步并行的接口队列,只要把这个传入到asyncLimit中就能实现异步并行

检查是否有正在运行的 runningPromise
runningPromise的作用:
判断当前是否已经有运行中的asyncLimit

如果有那么我们只需要继续往targetArray中加入数据即可,沿用之前的asyncLimit即之前的Promise 链。

如果没有说明asyncLimit函数已经执行完了,我们要新开一个asyncLimit函数去完成我们的并行限制。调用 asyncLimit 函数来处理目标数组中的元素,并基于此创建一个新的 Promise 链。

处理完成后,我们将结果存储在 resultArray 中,并重置目标数组和运行的 Promise。

总结

异步并行逻辑交由asyncLimit处理即可。
使用上来说,就只需要使用到接口的时候,调用asyncExecutetargetArray 加数据就行,默认会直接执行 asyncLimit 并创建一个promise链。
当我们往里面加一项promise链就会对应的多一项,当我们promise链执行完之后我们就会重置targetArrayrunningPromise
下次调用asyncExecute时,如果runningPromise不存在就重新走上面的逻辑,即直接执行 asyncLimit 并创建一个promise链,当runningPromise存在的情况下,每次使用asyncExecutetargetArray里面push参数即可。

本文转载于:

https://juejin.cn/post/7282733743910731833

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 

标签:item,--,前端,并发,running,Promise,asyncLimit,asyncExecute
From: https://www.cnblogs.com/smileZAZ/p/17730883.html

相关文章

  • COM 详尽
    1.ProcSharp第九版Chapter23例子。运行C++COMServer例子,regsvr32/s"dll路径”之后,C#工程引用COM找到的名称是COMServerlib,在注册表中计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\{8FCA0342-FAF3-4481-9D11-3BC613A7F5C6}计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Cla......
  • Codeforces Round 738 (Div. 2) B. Mocha and Red and Blue
    给一个字符串,包含字符\(B\),\(R\),\(?\)。其中\(?\)可能为\(B\)或\(R\)。规定不完美数为字符串中相同字符连续出现的次数,询问一个字符串的最少可能不完美数。观察到\(BR\)或\(RB\)需要尽可能多,于是考虑尽可能让相邻字符不同。容易证明从左往右扫和从右往左扫贡献......
  • Kubesphere安装DevOps时Jenkins报Readiness probe failed
    1.问题安装Kubesphere官方文档,启用DevOps组件,然后等待安装完后,发现devops-jenkins-xxxx-xxxx的pod一直起不来,describe一下,发现报错:Readinessprobefailed:Get"http://10.244.36.83:8080/login":dialtcp10.244.36.83:8080:connect:connectionrefused2.解决这种情......
  • 欧拉的源地址
    https://repo.openeuler.org/https://repo.openeuler.org/openEuler-22.03-LTS-SP1/     ......
  • Win32编程之远程注入(十七)
    一、VirtualAllocEx函数VirtualAllocEx 用于在另一个进程的虚拟地址空间中分配内存。这个函数通常与其他进程间内存操作函数一起使用,允许一个进程分配内存并将其映射到另一个进程的地址空间中。函数原型:LPVOIDVirtualAllocEx(HANDLEhProcess,LPVOIDlpAddress,SIZ......
  • 本地测试Spark的svm算法
    上一篇介绍了逻辑回归算法,发现分类效果不好,通过这次的svm发现是因为训练数据不行,于是网上找了部分训练数据,发现实际上分类效果还可以。    训练数据,第一个值是标签,下面的数据是某种花的相关特征。1|5.1,3.5,1.4,0.21|4.9,3,1.4,0.21|4.7,3.2,1.3,0.21|4.6,3.1,1.5,0.21......
  • 【持续更新】无穷的技艺
    无穷的技艺关于我对学习编程之后的有趣创作想象目录无穷的技艺搭建一个图书管理系统学习并构架神经网络算法null搭建一个图书管理系统学习并构架神经网络算法......
  • opencv ubuntu安装
    #######################################INSTALLOPENCVONUBUNTUORDEBIAN########################################--------------------------------------------------------------------|#SCRIPTOPTIONS......
  • Error: Could not find or load main class org.apache.zookeeper.server.quorum.Quor
    下载mavenapache-maven-3.9.4-bin.tar.gz解压tar-zxvfapache-maven-3.9.4-bin.tar.gz添加环境变量vim/etc/profileexportMAVEN_HOME=/usr/local/maven-3.9.4exportPATH=${PATH}......
  • thinkphp无法访问控制器如何解决
     TP6在初次安装时创建/app/admin/controller/Index文件时无法访问 默认的thinkphp6只有部分的内置拓展包括:自带的frameworkthinkthinkphp核心驱动think-filesystem文件储存驱动think-helper常用助手库think-orm数据库驱动think-trace页面Trace扩展 安装......