Promise.all的并发控制

本文探讨了在JavaScript中如何控制Promise.all的并发量。通过分析Promise.all的行为,提出在生成Promise时进行并发控制的解决方案,并展示了一段实现并发控制的代码示例,详细记录了异步请求的执行流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前几天睿联的面试官问了这样一个问题:如何控制Promise.all的并发量,当时回答的不好,于是经过一段时间的探索,解决了该问题

        首先我们要知道promise.all()的用法一般是是传入一个promise的数组,但是由于Promise的内部是同步代码,在Promise生成后其里面的异步请求就已发送,Promise.all()只是对其结果的一个收集,那么我们要想进行并发控制就必须在生成Promise的阶段进行并发控制下面是代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Promise.all()</title>
  </head>
  <body></body>
  <script>
    // 并发量
    const limit = 3;
    // 异步任务的参数数组,一般为url
    const array = [
      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 1,
      2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 1, 2,
    ];
    // 根据参数生成Promise的函数、一般为根据传入的url构造一个Promise,其内部封装一个异步请求
    const proFn = (item) => {
      return new Promise((resolve, reject) => {
        console.log(`开始生成${item}的异步请求          ····`);
        // 模拟异步请求
        setTimeout(() => {
          console.log(item);
          // 得到结果
          resolve(item);
          console.log(`${item}的异步请求执行完毕            √`);
        }, Math.random() * 1000);
      });
    };
    console.log(promiseAll(array, proFn, limit));

    // proFn返回一个封装了异步任务的promise
    function promiseAll(arr = [], proFn, limit) {

      // 当前正在遍历的坐标
      let index = 0;
      // 存放结果的数组
      let res = [];
      // 正在执行的数组
      let excuting = [];


      // 执行函数
      function enqueue() {
        // 当执行完毕之后返回resolve状态的promise
        if (index === arr.length) {
          return Promise.resolve();
        }

        // 依次取出一个元素
        const item = arr[index++];

         /* 此处then方法会立即返回一个promise,在then回调运行结束
         (promise生成完毕)之后才会变成resolved状态,且当时的promise与
         proFn生成的promise保持一致(1、状态一致;2、resolve或者reject的值一致)*/
        const p = Promise.resolve().then(() => proFn(item, arr)); 
        // 将其放到promise数组
        res.push(p);

        // 将e放入正在执行的数组,并且在p执行完成之后将当前执行的e删除掉
        const e = p.then(() => {
          excuting.splice(excuting.indexOf(e), 1);
        });
        excuting.push(e);

        // 让r为一个默认resolved状态的promise
        let r = Promise.resolve();
        // 如果执行数组满了的话,那就让r通过race等待改变状态
        if (excuting.length >= limit) {
          r = Promise.race(excuting);
        }
        // 等到r变为resolved状态(执行数组没满或者有一个已经执行完被删除了)再来递归调用enqueue
        return r.then(() => enqueue());
      }

      // 执行完成后,通过promise.all返回所有的结果
      return enqueue().then(() => Promise.all(res));
    }
  </script>
</html>

运行结果:

开始生成1的异步请求          ····

开始生成2的异步请求          ····

开始生成3的异步请求          ····

2

2的异步请求执行完毕            √

开始生成4的异步请求          ····

1

1的异步请求执行完毕            √

开始生成5的异步请求          ····

3

3的异步请求执行完毕            √

开始生成6的异步请求          ····

4

4的异步请求执行完毕            √

开始生成7的异步请求          ····

5

5的异步请求执行完毕            √

开始生成8的异步请求          ····

6

6的异步请求执行完毕            √

开始生成9的异步请求          ····

7

7的异步请求执行完毕            √

开始生成10的异步请求          ····

8

8的异步请求执行完毕            √

开始生成11的异步请求          ····

10

10的异步请求执行完毕            √

开始生成12的异步请求          ····

9

9的异步请求执行完毕            √

开始生成13的异步请求          ····

11

11的异步请求执行完毕            √

开始生成14的异步请求          ····

12

12的异步请求执行完毕            √

开始生成15的异步请求          ····

15

15的异步请求执行完毕            √

开始生成16的异步请求          ····

13

13的异步请求执行完毕            √

开始生成17的异步请求          ····

14

14的异步请求执行完毕            √

开始生成18的异步请求          ····

17

17的异步请求执行完毕            √

开始生成19的异步请求          ····

16

16的异步请求执行完毕            √

开始生成20的异步请求          ····

18

18的异步请求执行完毕            √

开始生成1的异步请求          ····

19

19的异步请求执行完毕            √

开始生成2的异步请求          ····

20

20的异步请求执行完毕            √

开始生成3的异步请求          ····

1

1的异步请求执行完毕            √

开始生成4的异步请求          ····

4

4的异步请求执行完毕            √

开始生成5的异步请求          ····

3

3的异步请求执行完毕            √

开始生成6的异步请求          ····

2

2的异步请求执行完毕            √

开始生成7的异步请求          ····

6

6的异步请求执行完毕            √

开始生成8的异步请求          ····

5

5的异步请求执行完毕            √

开始生成9的异步请求          ····

8

8的异步请求执行完毕            √

开始生成10的异步请求          ····

7

7的异步请求执行完毕            √

开始生成11的异步请求          ····

9

9的异步请求执行完毕            √

开始生成12的异步请求          ····

12

12的异步请求执行完毕            √

开始生成13的异步请求          ····

10

10的异步请求执行完毕            √

开始生成14的异步请求          ····

11

11的异步请求执行完毕            √

开始生成15的异步请求          ····

13

13的异步请求执行完毕            √

开始生成16的异步请求          ····

15

15的异步请求执行完毕            √

开始生成17的异步请求          ····

16

16的异步请求执行完毕            √

开始生成18的异步请求          ····

18

18的异步请求执行完毕            √

开始生成19的异步请求          ····

14

14的异步请求执行完毕            √

开始生成20的异步请求          ····

17

17的异步请求执行完毕            √

开始生成1的异步请求          ····

19

19的异步请求执行完毕            √

开始生成2的异步请求          ····

1

1的异步请求执行完毕            √

20

20的异步请求执行完毕            √

2

2的异步请求执行完毕            √

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值