当前位置: 首页 > 知识库问答 >
问题:

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

戚阳
2024-04-29

写了一段 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  });}

目前的一个结果:

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

Help,Thanks!

共有3个答案

彭鹭洋
2024-04-29

根据 @木木剑光 的解答进行略微的修改。

const createPool = (task, { concurrency } = {}) => {  const pool = [];  let runningCount = 0;  const promiseMap = new Map();  return function (i) {    return new Promise((resolve, reject) => {      pool.push({ id: i, fn: () => task(i) });//modify1: 队列中以{id,fn}的方式存储任务      promiseMap.set(i, { resolve, reject });      function run() {        while (pool.length && runningCount < concurrency) {          const runTask = pool.shift();          runningCount++;          runTask            .fn()            .then((val) => {              const { resolve } = promiseMap.get(runTask.id); // modify2: 使用当前任务的 id 去获取其 promise 的 resolve              resolve(val);            })            .catch((e) => {              const { reject } = promiseMap.get(runTask.id);// modify3: 使用当前任务的 id 去获取其 promise 的 reject              reject(e);            })            .finally(() => {              runningCount--;              run();            });        }      }      run();    });  };};// 简化场景,模拟业务调用const originFetchData = (id) => {  return new Promise((resolve,reject) => {    setTimeout(() => (id % 2 === 0 ? resolve(id) : reject(id)), 2000);  });};const fetchData = createPool(originFetchData, { concurrency: 5 });for (let i = 0; i < 20; i++) {  fetchData(i).then((response) => {    console.log("response ---", response); // TODO problem : 想在这里得到每个 task 的 response  }).catch(e=>console.error('err--',e));}

结果正常了。

万喜
2024-04-29

你在 createPool 里面不是限制了执行次数么

while (pool.length && runningCount < concurrency)...

然后你初始化 fetchData 的时候 限制了 执行 5 次

const fetchData = createPool(originFetchData, { concurrency: 5 })
萧阳波
2024-04-29

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();    });  };};
 类似资料:
  • 浏览器端,怎么发一个 http2 的请求?

  • 1、react ant design tree异步加载onLoadData,里面写了一个向后台发请求的方法,为什么每次会发两次请求?你们是怎么写的? 2、还有我是异步加载,每次展开点击一下,会后台请求查询数据,节点多了以后页面会很卡,请问该怎么解决??看文档加了虚拟高度,我加了以后,展开2次以后,树会消失,height width都变成0了,所以看不见树了

  • 问题内容: import scrapy from selenium import webdriver 此解决方案效果很好,但是它对相同的URL两次请求,一个是scrapy Scheduler,另一个是Selenium Web驱动程序。 与没有selenium的苛刻要求相比,完成这项工作将花费两倍的时间。如何避免这种情况? 问题答案: 这是解决这个问题的有用技巧。 *为硒 *创建一个Web服务 ,在

  • 我有两个实体A和B,其中A拥有B类的属性“B ”( A和B之间一对一的关联) 我想做一个Rest调用来保存A实体的实例,并将b param作为url传递,该url在json中给出类似的东西: /b/2引用B储存库的findById rest资源。 当我执行A Repository的这个目标rest resource save()(通常是对url/restapi/A的POST请求)时,它工作得很好,

  • 我正在开发我的第一个基于spring的项目样本。下面是我的dispatcher-servlet.xml的摘录。 我尝试向以下url发布帖子,但它似乎从未到达相应的控制器类: 网址: post<code>{“testID”:“1”} 就是回应。 为什么我得到400错误,尽管一切似乎都在地方?

  • 如图所示在html页面,通过script标签引入的publickKey文件 这里面需要发送get请求,请求服务器上一份加密key,然后在引入的其他js文件中使用 所以我需要确保 先拿到get请求的结果,再走下一个js 文件中的内容 publickKey.js 中用了 asynac await 但是还是先执行了下面的js 文件,导致没有拿到key , 现在需要先拿到get请求的结果,再走下一个js