Как заменить несколько асинхронных / ожидающих вызовов на Promise.all?

#javascript #node.js #json #promise #async-await

#javascript #node.js #json #обещание #async-await

Вопрос:

У меня есть следующий 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 amp;amp; firstData.rows[i]['myservice1Id'] !== null) {
            const aRes = await myService2(hostName);
            firstData.rows[i]['mylist'] =
                    aRes[0].dataValues;
        }
        if (hostName !== null amp;amp; firstData.rows[i]['type'].includes('type1')) {
            const oRes = await myService3(hostName);
            firstData.rows[i]['ores'] = oRes.rows[0];
        }
        if (hostName !== null amp;amp; 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 для выполнения всех 3 if blocks parallel -х входов?
Если да, то как будет выглядеть обновленный код при использовании Promise.all ?

Ответ №1:

Простейшей настройкой было бы поместить каждое обещание в массив внутри if s:

 const proms = [];
if (hostName !== null amp;amp; 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
 

Комментарии:

1. не могли бы вы подсказать, где включить for loop ?

2. Там же, где он находится в вашем текущем коде. С кодом в ответе вы будете выполнять до 3 параллельных запросов, что довольно разумно, я бы не хотел выходить за рамки этого обычно, хотя это зависит от того, насколько параллелизм может обрабатывать служба.

3. В конце концов, что я должен делать return proms; ?

4. Нет, вам просто нужно await Promise.all(proms); , как в ответе, а затем после этого эта строка будет полностью заполнена

5. Я получаю сообщение об ошибке 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 Источник блога Medium