js 并发控制怎么依次得到每个请求的结果?

作者站长头像
站长
· 阅读数 14

写了一段 js 控制并发数的逻辑,已经实现了控制并发,但是我想在最下面的 for 循环中得到每个 task 执行后的 response。 目前只能得到最开始 5 个的,这是为什么?

const createPool = (task, { concurrency } = {}) => {
  const pool = [];
  let runningCount = 0;
  return function (i) {
    return new Promise((resolve, reject) => {
      pool.push(() => task(i));
      function run() {
        while (pool.length && runningCount < concurrency) {
          const runTask = pool.shift();
          runningCount++;
          runTask()
            .then((val) => {
              resolve(val)
            })
            .catch((e) => reject(e))
            .finally(() => {
              runningCount--;
              run();
            });
        }
      }
      run();
    });
  };
};

// 简化场景,模拟业务调用
const originFetchData = (id) => {
  return new Promise((resolve) => {
    setTimeout(() => resolve(id), 2000);
  });
};
const fetchData = createPool(originFetchData, { concurrency: 5 });

for (let i = 0; i < 100; i++) {
  fetchData(i).then((response) => {
    console.log("response ---", response);  // TODO problem : 想在这里得到每个 task 的 response
  });
}

目前的一个结果:js 并发控制怎么依次得到每个请求的结果?

我知道可以用一个数组 or 对象存放批处理的结果,但是现在的场景需要单独处理每个 response

Help,Thanks!

回复
1个回答
avatar
test
2024-06-19

100次 for 循环是同步执行的,也就是说一开始就会产生 100 个 并发任务,但由于并发数的控制,只能进去 5 个,后 95 个的 Promise 都被丢弃了,当后面 95 次 异步任务 触发的时候实际上都是用的前面 5 次 闭包 中的 reslovereject

因此这里需要用一个 map 缓存一下 Promiseresolvereject

const createPool = (task, { concurrency } = {}) => {
  let runningCount = 0;
  const pool = [];
  const promiseMap = new Map();

  return function (i) {
    return new Promise((resolve, reject) => {
      promiseMap.set(i, { resolve, reject });

      pool.push(() => task(i));

      function run() {
        while (pool.length && runningCount < concurrency) {
          const runTask = pool.shift();
          runningCount++;
          runTask()
            .then((val) => {
              const { resolve } = promiseMap.get(val)
              resolve(val)
            })
            .catch((e) => reject(e))
            .finally(() => {
              runningCount--;
              run();
            });
        }
      }
      run();
    });
  };
};
回复
likes
适合作为回答的
  • 经过验证的有效解决办法
  • 自己的经验指引,对解决问题有帮助
  • 遵循 Markdown 语法排版,代码语义正确
不该作为回答的
  • 询问内容细节或回复楼层
  • 与题目无关的内容
  • “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容