如何用 Promise.all 替换多个 async/await 调用?

Posted

技术标签:

【中文标题】如何用 Promise.all 替换多个 async/await 调用?【英文标题】:How to replace multiple async/await calls with Promise.all? 【发布时间】:2021-03-19 08:51:44 【问题描述】:

我有以下 try/catch 块,它正在进行 3 个不同的 api 调用。 以下代码运行良好,但当firstData 具有大型数据集时,执行需要花费大量时间。

try 
    const firstData = await myservice1.myservice1Func();

    for(let i=0; i<firstData.total; i++)
        const hostName = firstData.rows[i]['hostname'];
        if (hostName !== null && firstData.rows[i]['myservice1Id'] !== null) 
            const aRes = await myService2(hostName);
            firstData.rows[i]['mylist'] =
                    aRes[0].dataValues;
        
        if (hostName !== null && firstData.rows[i]['type'].includes('type1')) 
            const oRes = await myService3(hostName);
            firstData.rows[i]['ores'] = oRes.rows[0];
        
        if (hostName !== null && firstData.rows[i]['type'].includes('type2')) 
            const vRes = await myService4(hostName);
            firstData.rows[i]['vRes'] = vRes.rows[0];
        
    
    return firstData;
 catch (err) 
    console.log(err);

这里,

const firstData = 

  "total": 2,
  "rows": [
    
      "hostname": "abc.com",
      "ipAddress": "11.11.11.11",
      "myservice1Id": "ee0f77c9-ef15",
      "type": "type1"
    ,
    
      "hostname": "cde.com",
      "ipAddress": "12.12.12.12",
      "type": "type2",
      "myservice1Id": null
    
  ]
   


const aRes = 
[
  
        "listType": "list1",
        "createdAt": "2020-12-07"
  
]


const oRes =

  "rows": [
    
      "status": "FAIL"
    
  ]



const vRes =

  "rows": [
    
      "status": "FAIL"
    
  ]

firstData的最终返回值如下:


  "total": 2,
  "rows": [
    
      "hostname": "abc.com",
      "ipAddress": "11.11.11.11",
      "myservice1Id": "ee0f77c9-ef15",
      "type": "type1",
      "oRes": 
        "status": "PASS"
      ,
      "mylist": 
        "listType": "list1",
        "createdAt": "2020-12-07"
      
    ,
    
      "hostname": "cde.com",
      "ipAddress": "12.12.12.12",
      "type": "type2",
      "myservice1Id": null,
      "vRes": 
        "status": "FAIL"
      
    
  ]
   

在这里,需要注意的一点是所有 3 个if blocks 都可以并行执行,因为它们是相互独立的。 我可以使用Promise.all 来执行parallel 中的所有3 个if blocks 吗? 如果是,更新后的代码使用Promise.all 会是什么样子?

【问题讨论】:

【参考方案1】:

最简单的调整是将每个 Promise 推送到 ifs 内的数组:

const proms = [];
if (hostName !== null && firstData.rows[i].myservice1Id !== null) 
    proms.push(
      myService2(hostName)
        .then(aRes => firstData.rows[i].mylist = aRes[0].dataValues)
    );

// other ifs changed around the same way

await Promise.all(proms);

您还可以通过只检查一次hostName 来简化代码,看起来您正在迭代整个数组,这可以通过调用迭代器更轻松地完成:

try 
    const firstData = await myservice1.myservice1Func();
    for (const row of firstData.rows) 
        const hostName = row.hostname;
        if (hostName === null) continue;
        const proms = [];
        if (row.myservice1Id !== null) 
            proms.push(
                myService2(hostName)
                    .then(aRes => row.mylist = aRes[0].dataValues)
            );
        
        // etc

【讨论】:

您能否建议在哪里包含for loop 当前代码中的相同位置。使用答案中的代码,您最多可以发出 3 个并行请求,这是非常合理的,我通常不想超出这个范围,尽管这取决于服务可以处理多少并行度。 最后,我应该做return proms;吗? 不,您只需要答案中的await Promise.all(proms);,然后在那之后,该行将被完全填充 我收到一个错误TypeError: Cannot read property 'dataValues' of undefined 你能告诉我在哪里添加未定义的检查吗?【参考方案2】:

您好,您有一些代码改动,

for(let i=0; i<firstData.total; i++)
    const hostName = firstData.rows[i]['hostname'];

     //check if condition inside the service and return a null (a promise)
     Promise.all([myService2(hostName), myService3(hostName), myService4(hostName)]).then((values) => 
        console.log(values);
        //[resutl1,null,result3]
     );

现在的问题是你必须等到最慢的迭代完成, 你可以通过使用承诺池来解决这个问题, @supercharge/promise-pool

MDN PromiseMedium Blog Source

【讨论】:

以上是关于如何用 Promise.all 替换多个 async/await 调用?的主要内容,如果未能解决你的问题,请参考以下文章

Roslyn - 如何用多个节点替换多个节点?

在 Ruby 中,如何用可能的多个元素替换数组中的元素?

如何用JavaScript替换数组中间隔多个参数

如何用正则表达式替换多个匹配项/组?

如何用 sed 替换缩小的 javascript 文件中的多个值 [重复]

如何用regexp_replace在mysql中用特定字符替换多个字符?