2015-04-24 7 views
1

На Form Action типа POST мы извлекаем все значения в Node.JS/Express и пытаемся сохранить его в MongoDB.async.waterfall внутри петли для петли выходит из цикла петли

Скрытое поле определяет длину свойства из фреймворка javascript, и его значение обновляется как значение скрытого поля.

Эта длина используется в бэкэнде (узле) для перебора списка элементов.

У меня есть async.waterfall функция и for loop работает внутри него, как это.

async.waterfall([ 
function(callback){ 
     var itemLength = req.body.itemLength; 
     var itemProp,itemComponent; 
     var destination; 
     var destinationsArray =[]; 

     for(var k=1; k<=itemLength; k++){ 

      destination = new Destination({ 
       name: req.body['destinationName'+k], 
      }); 

      itemComponent = { 
       "itemCompProp" : req.body['itemCompProp'+k] 
      }; 


      itemProp = new ItemProp({ 
       itemComponent: itemComponent 
      }); 

      itemProp.save(function(err,itemPropSaved){ 
       destination.newProperty = itemPropSaved._id 

       destination.save(function(err,destinationSaved){ 
       if(err){ 
        console.log("Error== " + err); 
       } 
       else{ 
        destinationsArray.push(destinationSaved._id); 
       } 
       }); 

      }); 
     }// End of For 
    callback(null,destinationsArray); 
}, 
function(destinationsArray,callback){ 
    var brand = new Brand({ 
    name : req.body.brandName, 
    }); 

    brand.save(function(err,brandSaved){ 
     if(err){ 
      console.log("Error== " + err); 
     }else{ 
      console.log('Brand Saved'); 
     } 
    }); 
    callback(null); 
} 
], function (err, status) { 
    if(err){ 
    req.flash('error', { 
      msg: 'Error Saving Brands' 
     }); 

    console.log("Error : " + err); 
    } 
    else{ 
     console.log("Brand Saved."); 
     req.flash('success', { 
      msg: 'Brand Successfully Added!' 
     }); 
    } 
}); 

res.redirect('/redirectSomewhere'); 

Когда мы управляем этим, destinationsArray возвращается первым в null, в отличие от прохождения через for loop, а затем возвращая правильное значение destinationsArray по длине (itemLength) направлений.

Мы хотим, чтобы процесс был синхронным. Мы также попытались использовать закрытие упаковки for Loop, но безрезультатно.

Мы не можем использовать async.eachSeries вместо for Loop как я просто итерации числовом собственности, и мы не имеем никакого documents to iterate over

Любое допустимое решения для запуска for Loop внутри async.waterfall?

Cheers and Thanks in Advance.

ответ

2

Есть несколько проблем с кодом, у вас там:

  1. где обратные вызовы получил колл.
  2. где res.redirect() получил вызов.
  3. для петля.

save() является асинхронным. Обычный для цикла будет продолжаться, не дожидаясь завершения всех вызовов save(). Вот почему destinationArray пуст. Как вы сказали, вы не можете использовать async.eachSeries(), так как вы повторяете числовое свойство. Тем не менее, вы на правильном пути. Async.whilst() делает именно это. Ниже приведен пересмотренный код с Async.whilst() и правильными адресами вызовов обратных вызовов:

async.waterfall([ 
    function(callback){ 
    var itemLength = req.body.itemLength; 
    var itemProp,itemComponent; 
    var destination; 
    var destinationsArray =[]; 
    var k = 1; // 1st part of for loop: for(k=1; k<=itemLength; k++) 

    async.whilst(
     function() { 
     return k <= itemLength; // 2nd part of for loop: for(k=1; k<=itemLength; k++) 
     }, 
     function(whilstCb) { 
     destination = new Destination({ 
      name: req.body['destinationName'+k] 
     }); 

     itemComponent = { 
      "itemCompProp" : req.body['itemCompProp'+k] 
     }; 

     itemProp = new ItemProp({ 
      itemComponent: itemComponent 
     }); 

     itemProp.save(function(err,itemPropSaved){ 
      destination.newProperty = itemPropSaved._id 

      destination.save(function(err,destinationSaved){ 
      if(err){ 
       console.log("Error== " + err); 
      } else { 
       destinationsArray.push(destinationSaved._id); 
      } 
      k++; // 3rd part of for loop: for(k=1; k<=itemLength; k++) 
      whilstCb(null); 
      }); 
     }); 
     }, 
     function(err) { 
     // It gets here once the loop is done 
     console.log(destinationsArray); // This array should have all the values pushed 
     callback(null, destinationsArray); 
     } 
    ); 
    }, 
    function(destinationsArray,callback){ 
    var brand = new Brand({ 
     name : req.body.brandName 
    }); 

    brand.save(function(err,brandSaved){ 
     if(err){ 
     console.log("Error== " + err); 
     } else { 
     console.log('Brand Saved'); 
     } 
     callback(null); 
    }); 
    } 
], function (err, status) { 
    if(err){ 
    req.flash('error', { 
     msg: 'Error Saving Brands' 
    }); 
    console.log("Error : " + err); 
    } else { 
    console.log("Brand Saved."); 
    req.flash('success', { 
     msg: 'Brand Successfully Added!' 
    }); 
    } 
    res.redirect('/redirectSomewhere'); 
}); 
+0

Работает как очарование. Отлично. Благодаря тонну! Не знал о 'async.whilst'. – theChinmay

1

Проблема связана с callback(null, destinationsArray);, получившим вызов за пределами for loop, не проверив сначала, чтобы цикл был закончен.

Попробуйте заменить callback(null, destinationsArray); с чем-то вроде этого:

if (itemLength > 0 && destinationsArray.length === k - 1) { 
    callback(null, destinationsArray); 
} else { 
    callback(true); 
} 

Вышеуказанные проверки, чтобы убедиться, что destination.save() будет завершена надлежащее количество раз успешно.

Я действительно предпочитаю метод, предложенный djskinner. Однако из-за console.log(), который возникает при ошибке save(), обратный вызов destinationsArray может содержать неправильное количество элементов. Чтобы исправить это, вы можете быть уверены, что замените console.log("Error== " + err); чем-то наподобие callback(err), чтобы закончить водопад с возвращенной ошибкой. Кроме того, проверка k === itemLength неправильно учитывает правильное количество элементов, которые должны быть сохранены. Это должно быть заменено на k === destinationsArray.length.

Я внесла изменения, чтобы исправить это и опубликовал обновленную версию ниже.

destination.save(function(err, destinationSaved){ 
    if (err) { 
     callback(err); 
    } 
    else { 
     destinationsArray.push(destinationSaved._id); 
     if (k === destinationsArray.length) { 
      callback(null, destinationsArray); 
     } 
    } 
}); 

--EDIT-- мне очень нравится решение, которое Бен публикуемое с помощью whilst(). Это позволяет создать цикл, в котором итерации выполняются последовательно. Для получения дополнительной информации просмотрите страницу npm here.

1

Это не столько цикл for, который вызывает у вас проблемы, но и то, что save - это асинхронная операция.Цикл for завершается, и обратный вызов выполняется до того, как любой из save обратных вызовов имеет возможность завершить.

Что вы хотите сделать, это вызвать обратный вызов async.waterfall после того, как были выполнены все обратные вызовы сохранения назначения. Что-то вроде:

  destination.save(function(err,destinationSaved){ 
      if(err){ 
       console.log("Error== " + err); 
      } else { 
       destinationsArray.push(destinationSaved._id); 
       if (k === itemLength) { 
        // all destination callbacks have completed successfully 
        callback(null, destinationsArray); 
       } 
      } 
      }); 
Смежные вопросы