ES6 号;嵌套 Promise.all(也许?

ES6; Nested Promise.all (Maybe?)

提问人:Scott 提问时间:11/4/2023 最后编辑:Scott 更新时间:11/4/2023 访问量:50

问:

帖子标题可能并不完全准确,因为我甚至不确定我的方法是否是最好的方法。我会让社区来决定。

在 React 应用程序中使用 Javascript(ES6)。 示例数据(原始):

dataArray = 
[
{"uid": 3, "region": "bru", "hostname": "bru-002.domain.com", "status": "active"}, 
{"uid": 1, "region": "bru", "hostname": "bru-001.domain.com", "status": "disabled"},
{"uid": 4, "region": "apc", "hostname": "apc-001.domain.com", "status": "active"}
];
  • 每个数组中的对象键名称都是静态的。我们可以假设如果有一个对象,它将包含键名称“hostname”等。
  • 实时数据将包含每个区域的数百个条目和数十个其他对象字段(键)。与代码无关,仅供参考。

最终目标是对每个主机执行 HTTP 请求,并从请求中收集结果。该功能已经编写并正在使用中。 我遇到的脑洞大开的是将这些调用分成“按区域划分块”的 CR。换句话说,执行第一个区域的所有调用,完成后,启动下一个区域的调用,依此类推。所有调用的最终结果将进入 setState()。

现有代码使用 Promise 而不是 async/await。大多数时候我使用 await,但在某些情况下,我确实喜欢 Promise 与 .then 结合提供的清晰的代码可读性。

这是当前正在使用的代码片段。它位于 useEffect 块中。

...
Promise.all(
  dataArray.map(function(host) {
    if (host['status'] !== 'disabled') {
     // pinger() is a function that makes the calls.
     // Assume it returns what we want.
      return pinger(host.hostname); 
    }
    return ''; // Default return to satisfy map function
  }))
  .then(result => {
    setPingedData(result); // Using a React set State
  }
);
...

上面的代码有效,但整体有效。为了将其分开,我首先创建了一个新对象,该对象最终如下所示:

sortedByRegionObj = 
{ "bru": [{"uid": 3, "hostname": "bru-002.domain.com", "status": "active"}, 
          {"uid": 1, "hostname": "bru-001.domain.com", "status": "disabled"}],
  "apc": [{"uid": 4, "hostname": "apc-001.domain.com", "status": "active"}]
};
  • 顶级对象键名称表示“区域组”。
  • 区域名称将是动态的。我们只能假设如果存在一个区域,该区域的值将是 1 个或多个对象的数组。
  • 数组表示我们希望如何对 HTTP 调用进行分组。该数组将是一组 Promise。

这个想法是使用类似的东西遍历对象:

for (const regionArray of Object.values(sortedByRegionObj)) {
 Promise.all(
          regionArray.map(function(host) {
...

(上面的代码是无效的 - 只是描述一般的想法)

并创建 Promise 数组,然后使用 Promise.all...但后来我迷路了。难倒我的部分是在尝试为 Promise.all 创建数组时具有动态数量的对象...... 我无法思考如何做到这一点。

思潮?

JavaScript ES6-承诺

评论

0赞 Krokodil 11/4/2023
那么,您是否有一个函数可以通过接收“集体”对象数组来创建按区域名称分隔的对象?
2赞 Bergi 11/4/2023
"倒我的部分是在尝试为 Promise.all 创建数组时具有动态数量的对象“——但你已经做得很好了吗?您的工作代码处理具有动态对象数的对象,带有 .dataArrayregionArray
2赞 Bergi 11/4/2023
要“执行第一个区域的所有调用,完成后,然后启动对下一个区域的调用”,请使用 in 循环。用承诺来做到这一点是可能的,但要丑陋得多。await
0赞 Marisa Kirisame 11/4/2023
我会使用 Array.filter 而不是 map,您可能会在映射数组中获得一些未定义的元素。
0赞 slebetman 11/4/2023
The above code is not valid- just depicting the general idea——我认为你错了。你写的代码是你问题的答案,只是你忘记了等待-- .你基本上已经在不知不觉中回答了自己的问题。你被难住了,因为你忘记了等待Promise.allfor (.....) { let regionData = await Promise.all(....

答:

0赞 Hatem 11/4/2023 #1

可能是这样的:

const dataArray = [
  { uid: 3, region: "bru", hostname: "bru-003.domain.com", status: "active" },
  { uid: 3, region: "bru", hostname: "bru-002.domain.com", status: "active" },
  { uid: 1, region: "bru", hostname: "bru-001.domain.com", status: "disabled" },
  { uid: 4, region: "apc", hostname: "apc-001.domain.com", status: "active" },
];

const pingHosts = async (data) => {
  const regions = new Set(data.map((d) => d.region));
  const results = {};
  for (const region of regions) {
    results[region] = await Promise.all(
      data
        .filter((d) => d.status !== "disabled" && d.region === region)
        .map((host) => pinger(host.hostname)),
    );
  }
  return results;
};

// simulate network request
const pinger = (hostname) => new Promise((resolve) => {
  setTimeout(() => {
    console.log(`pinger: ${hostname}`);
    resolve(`pinger: ${hostname}`);
  }, 500);
})

console.log('results: ', await pingHosts(dataArray));

演示:https://livecodes.io/?x=id/mndyfu329vr&console=open