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

Я сделал несколько запросов mongoDB в цикле. и я хочу отправить все результаты в виде одного массива данных. Но когда я просто использую return для отправки данных, он просто возвращает 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 возвращается неопределенным.

1 ответ

Решение

Давайте начнем с общего правила использования обещаний:

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

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

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

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

В нашем случае это легко с помощью помощников по взаимодействию узлов Q:

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

Теперь у нас есть только обещания, и нам больше не нужны отсрочки. Третье правило вступает в игру:

Все, что делает что-то с асинхронным результатом, переходит в .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()мы получим несколько обещаний, чьи асинхронные задачи выполняются параллельно и разрешаются в неизвестном порядке. Мы хотим дождаться их всех - то есть получить обещание, которое разрешается со всеми результатами, когда каждая из задач завершена.

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

Проверьте 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.

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