面试官问:如果有100个请求,你如何使用Promise控制并发?

const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));

  const makeRequest = async (id) => {
    console.log(`Request ${id} started`);
    await sleep(Math.random() * 2000);  // 模拟网络请求
    console.log(`Request ${id} completed`);
    return id;
  };

  const concurrentRequests = async (requests, maxConcurrency) => {
    let currentIndex = 0;
    const results = [];
    const pendingPromises = [];

    const enqueue = () => {
      if (currentIndex >= requests.length) {
        return Promise.resolve();
      }

      const requestIndex = currentIndex++;
      const request = requests[requestIndex];
      const promise = request().then(result => {
        results[requestIndex] = result;
      });

      pendingPromises.push(promise);

      const removePromise = () => {
        pendingPromises.splice(pendingPromises.indexOf(promise), 1);
      };

      promise.then(removePromise).catch(removePromise);

      const nextPromise = promise.then(() => enqueue());

      if (pendingPromises.length >= maxConcurrency) {
        return Promise.race(pendingPromises).then(() => nextPromise);
      } else {
        return nextPromise;
      }
    };

    await enqueue();

    return Promise.all(pendingPromises).then(() => results);
  };

  const requests = Array.from({ length: 100 }, (_, i) => () => makeRequest(i + 1));

  concurrentRequests(requests, 5).then(results => {
    console.log('All requests completed');
    console.log(results);
  });

模拟请求:

makeRequest 是一个模拟网络请求的函数,接受一个 id 参数,模拟请求完成需要一段时间(通过 sleep 函数实现)。
控制并发请求:

concurrentRequests 函数负责控制并发请求的数量。它接受两个参数:requests 是一个函数数组,每个函数返回一个 Promise;maxConcurrency 是最大并发请求数。
请求调度:在这里插入代码片

使用 currentIndex 追踪当前请求的位置。
results 用于存储每个请求的结果。
pendingPromises 存储当前正在进行的请求。
enqueue 函数处理请求队列,根据 maxConcurrency 控制并发请求数。
执行并发请求:

使用 Promise.race 等待其中一个请求完成,以便在完成后继续处理新的请求。
启动请求:

生成包含100个请求的数组,每个请求都是一个返回 Promise 的函数。
调用 concurrentRequests 函数并传入请求数组和最大并发数(5)。

更简单的方法!

维护一个运行池一个等待队列,出一个进一个,控制运行池的大小

const pool = new Set();
const waitQueue = [];
max = 10 //最大并发数
const request = v => {
    return new Promise((res, rej) => {
        
        const isfull = pool.size > max;
        const fn = () => {
            const request = v();
            request.finally(() => {
                pool.delete(fn);
                const next = waitQueue.shift();
                next && pool.add(next);
                next?.(); //相当于执行fn

                request.then(res);
                request.catch(rej);
                return request;
            })

        }
        if (isfull) {
            waitQueue.push(fn);
        } else {
            pool.add(fn);
            fn();
        }
    })
}

let arr = [];
//模拟一步请求
for (let i = 0; i < 100; i++) {
    arr.push(() => new Promise((resolve) => {
        setTimeout(() => {
            console.log('done', i);
            resolve();
        }, 100 * i);
    }));
};

arr.map((v, index) => { request(v).then(() => { console.log(index) }) })

解释:
isfull表示运行池是否满了,满了就把这个请求放在waitQueue中,没有满就放在运行池pool中直接执行,此时pool中有十个请求在同时进行, 十个异步请求但凡有一个执行完,就在从waitQueue拿出一个放在pool中继续执行,所以每个request都要定义finally后的回调

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值