Проблема при возврате данных, полученных из запросов БД, вызываемых в цикле

Я делаю несколько запросов mongoDB в цикле. и хочу отправить все результаты в виде одного массива данных. Но когда я просто использую возврат для отправки данных, он просто возвращает undefined и не ждет результатов всего запроса БД. Я также попытался использовать q.moulde, но такую ​​же проблему.

Код:

var getPrayerInCat = function(data){
    var result ;
    var finalData = [];
    if(data.length >0){
             data.forEach(function(data2){
                 var id= data2.id;
                 Prayer.find({prayerCat:id},function(err,prayer){
                     var deferred = Q.defer()
                     if (err) { // ...
                         console.log('An error has occurred');
                         // res.send(err);
                         result= finalData = err
                     } else {
                         if(!prayer){
                             // console.log(data2.id+'--0');
                             data2.prayersCount = 0;
                             result = deferred.resolve(finalData.push(data2))
                         } else {
                             // console.log(data2.id+'--'+prayer.length);
                             data2.prayersCount = prayer.length;
                             // console.log(prayer)
                             result =  deferred.resolve(finalData.push(data2))
                         } // else for data forward
                     }
                     deferred.promise;
                 })
                // deferred.resolve(finalData);

             })
             /*if(finalData.length > 0) { return finalData;}*/
        }
}

finalData возвращается undefined.

Ответ 1

Начнем с общего правила использования promises:

Каждая функция, которая делает что-то асинхронную, должна возвращать обещание

Какими функциями они являются в вашем случае? Это getPrayerInCat, обратный вызов forEach и Prayer.find.

Hm, Prayer.find не возвращает обещание, и это функция библиотеки, поэтому мы не можем ее модифицировать. Правило 2 вступает в игру:

Создайте немедленную оболочку для каждой функции, которая не

В нашем случае это легко с Q node -interfacing helpers:

var find = Q.nbind(Prayer.find, Prayer);

Теперь у нас есть только promises, и больше не нужно никаких отсрочек. Вступает в игру третье правило:

Все, что делает что-то с результатом async, переходит в .then обратный вызов

... и возвращает результат. Черт, этот результат может даже быть обещанием, если "что-то" было асинхронным! При этом мы можем написать полную функцию обратного вызова:

function getPrayerCount(data2) {
    var id = data2.id;
    return find({prayerCat:id})
//  ^^^^^^ Rule 1
    .then(function(prayer) {
//  ^^^^^ Rule 3
        if (!prayer)
            data2.prayersCount = 0;
        else
            data2.prayersCount = prayer.length;
        return data2;
//      ^^^^^^ Rule 3b
    });
}

Теперь у нас есть что-то более сложное: цикл. Повторно называя getPrayerCount(), мы получим несколько promises, чьи асинхронные задачи выполняются параллельно и разрешаются в неизвестном порядке. Мы хотим дождаться их всех, т.е. Получить обещание, которое решает все результаты, когда каждая из задач завершена.

Для таких сложных задач не пытайтесь найти свое решение:

Проверьте API вашей библиотеки

И там мы находим Q.all, что делает именно это. Написание getPrayerInCat теперь ветерок:

function getPrayerInCat(data) {
    var promises = data.map(getPrayerCount); // don't use forEach, we get something back
    return Q.all(promises);
//  ^^^^^^ Rule 1
}

Если нам нужно было что-либо сделать с массивом, разрешаемым Q.all, просто примените правило 3.