How to replace multiple async/await calls with Promise.all?

Viewed 177

I have the following try/catch block which is making 3 different api calls. The following code is working fine but it is taking lot of time to execute when firstData has large dataset.

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);
}

Here,

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"
    }
  ]
}

The final value of firstData returned is as following:

{
  "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"
      }
    }
  ]
}   

Here, one thing to notice is that all the 3 if blocks can be executed in parallel because they are independent of each other. Can I use Promise.all to execute all the 3 if blocks in parallel? If yes, how the updated code will look like using Promise.all?

2 Answers

Simplest tweak would be to push each Promise to an array inside the 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);

You could also make the code easier by making the hostName check only once, and it looks like you're iterating over the whole array, which can be done more easily by invoking the iterator:

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

Hi you have bit of code alterations,

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]
     });
}

Now the problem here is you have to wait until the slowest iteration to complete, You can fix that with promise pool use, @supercharge/promise-pool

MDN Promise Medium Blog Source

Related