翼度科技»论坛 编程开发 JavaScript 查看内容

前端如何控制并发请求举例详解

4

主题

4

帖子

12

积分

新手上路

Rank: 1

积分
12
前言

什么情况需要前端控制并发请求,在需要多次才能请求完所需数据的时候。比如接口一次返回,数据很多,让浏览器渲染卡顿甚至崩溃,这时候我们可以分批同时发出6个请求,这样就可以避免卡顿或者崩溃。
那么前端如何控制并发请求呢?

前端控制并发请求的关键思路

比如有 20 个请求,需要按照
  1. 3
复制代码
个一组,第一组请求完毕后再请求第二组,以此类推。
关键思路,把请求方法和请求参数使用一个数组存起来,然后每次请求3个,请求完毕后再请求下一个3个。每组请求返回后,把结果保存起来,等所有请求都返回后,再把所有结果返回。

api 设计


    1. pControl
    复制代码
    : 并发请求控制器, 传递最大并发数;
    1. add
    复制代码
    : 添加请求和参数;
    1. start
    复制代码
    : 开始请求,返回promise, 请求完毕后通过
    1. .then
    复制代码
    获取所有结果;

代码实现
  1. function pControl(limit) {
  2.   const taskQueue = [] // {task: Function, params: any[]}[]
  3.   return {
  4.     add,
  5.     start
  6.   }

  7.   function add(task, params) {
  8.     taskQueue.push({
  9.       task,
  10.       params
  11.     })
  12.   }

  13.   function start() {
  14.     return runAllTasks()
  15.   }

  16.   function runAllTasks() {
  17.     const allResults = []
  18.     return new Promise((resolve) => {

  19.       runTask()

  20.       function runTask() {
  21.         if (taskQueue.length === 0) {
  22.           // 递归结束
  23.           return resolve(allResults)
  24.         }
  25.         const needRunSize = Math.min(taskQueue.length, limit)
  26.         const tasks = taskQueue.splice(0, needRunSize)
  27.         const promises = tasks.map(({
  28.           task,
  29.           params
  30.         }) => task(params))
  31.         Promise.all(promises).then((resList) => {
  32.           allResults.push(...resList)
  33.           // NOTE 递归调用的位置很关键
  34.           runTask()
  35.         })
  36.       }
  37.     })
  38.   }
  39. }
复制代码
关键代码解读


    1. pControl
    复制代码
    : 这个函数返回一个对象,包含
    1. add
    复制代码
    1. start
    复制代码
    两个方法,
    1. add
    复制代码
    用来添加任务和参数,
    1. start
    复制代码
    用来开始请求,是一个闭包。
    1. runAllTasks
    复制代码
    : 返回一个
    1. promise
    复制代码
    ,然后在
    1. new Promise
    复制代码
    内部递归地执行
    1. runTask
    复制代码
    , runTask 通过
    1. Promise.all
    复制代码
    执行并发请求,在
    1. Promise.all().then()
    复制代码
    再次调用
    1. runTask
    复制代码
    ,实现一组请求返回,再执行第二组请求。
  1. 实现分组等待的关键是在 [code].then
复制代码
中递归调用。[/code]
  1. 思考: runAllTasks 可以使用循环实现吗?
复制代码
能,需要使用
  1. async 和 for 循环 + await
复制代码
  1. async function runAllTasks2() {
  2.   const allResults = []
  3.   const groupArr = []
  4.   let startIndex = 0
  5.   // 划分分组
  6.   while (startIndex < taskQueue.length) {
  7.     const arr = taskQueue.slice(startIndex, startIndex + limit)
  8.     groupArr.push(arr)
  9.     startIndex += limit
  10.   }
  11.   for (let index = 0; index < groupArr.length; index++) {
  12.     const pList = groupArr[index].map(({
  13.       task,
  14.       params
  15.     }) => task(params))
  16.     const res = await Promise.all(pList)
  17.     allResults.push(...res)
  18.   }
  19.   return allResults
  20. }
复制代码
  1. 在 for 中循环中不能使用 [code].then
复制代码
,否则下一次循环不会等待上一次循环。[/code]使用
  1. for of
复制代码
迭代实现:
  1. async function runAllTasks2() {
  2.   const allResults = []
  3.   const groupArr = []
  4.   let startIndex = 0
  5.   // 划分分组
  6.   while (startIndex < taskQueue.length) {
  7.     const arr = taskQueue.slice(startIndex, startIndex + limit)
  8.     groupArr.push(arr)
  9.     startIndex += limit
  10.   }
  11.   // 迭代分组
  12.   const it = groupArr.entries()
  13.   for (const [key, value] of it) {
  14.     const pList = value.map(({
  15.       task,
  16.       params
  17.     }) => task(params))
  18.     const res = await Promise.all(pList)
  19.     allResults.push(...res)
  20.   }
  21.   return allResults
  22. }
复制代码
循环和 Promise 结合是怎样使用的呢?
  1. for
复制代码
  1. while
复制代码
  1. for...of
复制代码
等命令式循环结构,想要在循环中实现等待效果,必须使用
  1. async
复制代码
函数包裹循环中的
  1. await
复制代码
,不能使用
  1. .then
复制代码
  1. forEach
复制代码
  1. map
复制代码
  1. filter
复制代码
等函数式循环结构,不支持等待效果,因为这些函数式循环结构是同步的,不支持等待。
  1. [code]async
复制代码
  1. 循环
复制代码
+
  1. await
复制代码
结合,实现循环之间等待效果。[/code]
  1. [code]promise.then
复制代码
  1. 递归
复制代码
结合,实现循环之间等待效果。[/code]
完善 api,让其更加易用


  • 设置默认参数:给
    1. pControl
    复制代码
    设置一个合适的默认值,设置为
    1. 6
    复制代码
    ,因为同一个域名在,浏览器的并发请求是 6 个。
  • start给回调:通过回调能拿到每个分组的请求结果和知道当前完成的请求数量。
这两个改进很简单。先看用法:
  1. const asyncTaskControl = pControl() // 默认 6
  2. asyncTaskControl.add(task, params1)
  3. asyncTaskControl.add(task, params2)
  4. // ...
  5. asyncTaskControl.add(task, params10)

  6. asyncTaskControl.start((res, doneSize) => {
  7.   // 获取每组请求的结果 和当前完成了多少请求
  8.   console.log(res) // [{index:number,result:data}]
  9.   console.log(doneSize)
  10. }).then(allResults => {
  11.   // 所有请求结果
  12.   console.log(allResults)
  13. })
复制代码
  1. start 回调有什么作用呢?
复制代码
方便使用者拿当前并发请求的结果,方便计算完成进度。

把上述功能封装成 p-control npm 包发布

npm: p-control
可通过
  1. npm i p-control
复制代码
下载使用。

小结


    1. .then
    复制代码
    和递归结合,实现异步任务之间等待;
    1. for
    复制代码
    1. while
    复制代码
    等循环和
    1. async
    复制代码
    +
    1. await
    复制代码
    结合使用,实现异步任务之间等待;
  • 使用
    1. Promise.all
    复制代码
    实现多个异步任务并发执行。
到此这篇关于前端如何控制并发请求的文章就介绍到这了,更多相关前端控制并发请求内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

来源:https://www.jb51.net/javascript/326792qgv.htm
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具