es6 вложенный параллельный цикл в for-of loop - PullRequest
0 голосов
/ 25 мая 2019

Итак, у меня есть массив (sources) с вложенными массивами (sources.feed), источники должны запускаться последовательно (один за другим), а sources.feed должен выполняться параллельно.

У меня естьудалось запустить их оба последовательно:

 for (const source of sources) {
     for (const feed of source.feeds) {
         await ProcessService.getData(feed, source)
     }
   }
console.log('Done processing.') //Triggers at the end (as expected)

И это моя последняя из нескольких попыток:

for (const source of sources) {
   await Promise.all(source.feeds.map(async (feed) => {
      await ProcessService.getData(feed, source)
   }))
}
console.log('Done processing.') //Triggers before it is done

Обновлено для ясности (должен вызываться только ProcessService.getData ())

getData: async function(data, source) {
    return new Promise(async (resolve, reject) => {
      try {
          ...
          resolve()
      } catch(e) {
          reject(e)
      }
   })
 }

Ответы [ 2 ]

4 голосов
/ 25 мая 2019

В вашем первом кодовом блоке (который, как вы сказали, работает, но последовательно) вы звоните ProcessService.getData, но во втором вы звоните RSSService.getRssAsync.

Если я предполагаю, что вы намеревались использовать ProcessService.getData и что он возвращает обещание (что, как я полагаю, должно произойти, вы сказали, что ваш первый блок кода работает, как и ожидалось), то для параллельной подачи каналов вы должны что-то сделать как ваш второй кодовый блок, но он не должен быть таким сложным:

for (const source of sources) {
    await Promise.all(source.feeds.map(feed => ProcessService.getData(feed, source)));
}
console.log('Done processing.');

Поскольку ProcessService.getData возвращает обещание, а то, что нам нужно для Promise.all, является массивом обещаний, мы не выполняем обратный вызов async, мы просто используем обещание, которое ProcessService.getData дает нам.

Это циклически просматривает источники, получая все каналы для первого источника (параллельно), затем все каналы следующего источника (параллельно) и т. Д.

Пример в реальном времени:

(async () => {
    const ProcessService = {
        getData(data, source) {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    try {
                        console.log(`Done with feed ${source.name} ${data}`);
                        resolve(/*...should have data here...*/)
                    } catch(e) {
                        reject(e)
                    }
                }, Math.random() * 500);
            });
        }
    }

    const sources = [
        {name: "Source 1", feeds: ["11", "12", "13"]},
        {name: "Source 2", feeds: ["21", "22", "23"]},
        {name: "Source 3", feeds: ["31", "32", "33"]},
        {name: "Source 4", feeds: ["41", "42", "43"]}
    ];

    for (const source of sources) {
        console.log(`Processing ${source.name}`);
        await Promise.all(source.feeds.map(feed => ProcessService.getData(feed, source)));
    }
    console.log('Done processing.');
})().catch(e => {
    console.error(`Error: ${e.message}`);
});
.as-console-wrapper {
    max-height: 100% !important;
}

Если вам действительно нужно использовать RSSService.getRssAsync, может показаться, что он не возвращает обещание, поскольку, если бы он это сделал, ваш код работал бы так, как ожидалось (даже если он мог бы быть проще). Чтобы преобразовать API обратного вызова в API обещаний, см. ответы на этот вопрос .


Ваша функция getData имеет несколько проблем:

  1. Поскольку вы явно создаете обещание, оно не должно быть функцией async. Вы используете функцию async, когда хотите использовать await для существующего обещания. Если вы создаете новое обещание, вам почти никогда не понадобится функция async.
  2. Функция, которую вы передаете в new Promise (функция исполнения обещания ) должна никогда быть async.
  3. Вы показываете, что звоните resolve() без аргументов. Это нормально, если вы действительно хотите выполнить обещание со значением undefined (для которого есть несколько случаев ограниченного использования), но если функция называется getData, она действительно должна возвращать данные, а не undefined.

Итак:

getData: function(data, source) {
//       ^---- no `async` here
    return new Promise((resolve, reject) => {
//                     ^---- no `async` here
      try {
          ...
          resolve(/*...should have data here...*/)
      } catch(e) {
          reject(e)
      }
   })
}

Также обратите внимание, что вы можете использовать синтаксис метода (похоже, он находится в инициализаторе объекта):

getData(data, source) {
//     ^
    return new Promise((resolve, reject) => {
      try {
          ...
          resolve(/*...should have data here...*/)
      } catch(e) {
          reject(e)
      }
   })
}
1 голос
/ 25 мая 2019

Для этого можно использовать Promise.all.

Ожидание каждой серии:

for (const source of sources) {
    const promises = [];

    for (const feed of source.feeds) {
        promises.push(ProcessService.getData(feed, source));
    }

    // Wait for the series to finish before continuing
    await Promise.all(promises);
}
console.log('Done processing.');

Ожидание всех вложенных элементов в конце:

const promises = [];
for (const source of sources) {
    for (const feed of source.feeds) {
        promises.push(ProcessService.getData(feed, source));
    }
}

// Wait for all of them at the end (instead of each series)
await Promise.all(promises);
console.log('Done processing.');
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...