Как дождаться нескольких асинхронных вызовов из цикла foreach?

Попытка вызвать несколько асинхронных функций и в результате получить результат как undefined.

Пробовал async.waterfall, но не смог заставить его работать.

Код:

const pendingData = [];

async.waterfall([
    function (callback) {
      WaitForApproval.find({}, (err,result) => {
        callback(null,result);
      });
    },
    function (result, callback) {
      result.forEach(data => {
        User.findOne({_id: data.uploadedBy}, (err,name) => {
          let total = {
            id: data._id,
            name: name.name,
            subject: data.subject,
            uploadOn: data.uploadedAt
          };
          pendingData.push(total);
        });
      });
      callback(null,'done');
    }
  ], function (err,result) {
    if(result === 'done') {   
      console.log(pendingData); // it is giving empty result.
    }
  });

Как дождаться асинхронной функции?

Просто замените result.forEach на async.series или async.parallel

Cody G 13.09.2018 20:42
1
2
1 683
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Проблема, с которой вы столкнулись, заключается в том, что вы выполняете асинхронные функции в неасинхронном цикле forEach.

Здесь у вас есть несколько вариантов:

  1. Сделайте вызов mongoDB рекурсивно - оберните этот запрос в функцию, которая вызывает себя после возврата запроса.

  2. Прочтите о пакетных операциях mongoDB - https://docs.mongodb.com/manual/reference/method/Bulk/

  3. Используйте шаблон async / await с каждым вызовом, объявляя функцию обратного вызова внутри асинхронного водопада как async, а затем используя await внутри функции для каждого запроса. По умолчанию forEach не является асинхронным. Если вы хотите по-прежнему использовать forEach, вы можете либо переписать его async (см. Ниже), либо использовать обычный цикл for:

async function asyncForEach(array, callback) {
  for (let index = 0; index < array.length; index++) {
    await callback(array[index], index, array)
  }
}

** Существуют дополнительные способы решения этой проблемы помимо того, что опубликовано здесь, но вот несколько вещей, которые будут работать при правильной реализации.

Это вызовет обратный вызов после завершения первого User.findOne. Слишком рано. Возможно, вы могли бы проверить длину pendingData: if (pendingData.length === result.length) callback(null, pendingData)

Mark 13.09.2018 20:32

Это дает ошибку, поскольку я использую forEach, для которого callback будет использоваться вне цикла.

Rupesh Yadav 13.09.2018 20:35

@MarkMeyer вы правы! Я не понимал, что здесь вызывается forEach - прошу прощения; Я быстро ответил на это, потому что только что столкнулся с той же проблемой на работе :)

AlexanderGriffin 13.09.2018 20:37

@MarkMeyer, этот трюк работает. Но наверняка есть и другой способ решить эту проблему.

Rupesh Yadav 13.09.2018 20:39

Я бы посоветовал вам сразу добавить призыв к callback(null,'done');pendingData.push(total);

вы правы, асинхронная природа сейчас усложняет вам задачу, но предположим, что вы использовали обещания и связали их вместе, это избавило бы вас от многих проблем.

однажды у меня была аналогичная проблема с асинхронным кодом, выходящим из строя, я хотел, поэтому я сделал небольшую настройку, используя настраиваемую функцию обещания (я добавил) и назвал ее порядком ... такая идея может решить вашу проблему, если вы можете ее применить к вашему коду правильно https://github.com/lalosh/Ideas/blob/master/promiseOrder.js

Ваш фрагмент великолепен, но есть несколько вещей, которые, я думаю, могут его улучшить: - Удалите целые числа журнала консоли, они лишние - Если вы используете архитектуру async / await вместе с обещанными функциями, вы можете избежать обратных вызовов .then ()

AlexanderGriffin 13.09.2018 20:52

Другие вопросы по теме