for 循环内的 async.waterfall 转义 for 循环

Posted

技术标签:

【中文标题】for 循环内的 async.waterfall 转义 for 循环【英文标题】:async.waterfall inside a for loop escapes the for loop 【发布时间】:2015-07-02 17:24:23 【问题描述】:

POST 类型的Form Action 上,我们获取Node.JS/Express 中的所有值并尝试将其保存到MongoDB

隐藏字段从前端 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 然后在一定长度(itemLength) 的目的地上返回正确的destinationsArray 值。

我们希望这个过程是同步的。我们还尝试使用包装for Loop 的闭包,但无济于事。

我们不能使用async.eachSeries 代替for Loop,因为我只是在迭代一个数字属性并且我们没有任何documents to iterate over

async.waterfall 内运行for Loop 的任何可行解决方案?

提前干杯和感谢。

【问题讨论】:

【参考方案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 提出的方法。但是,由于出现save() 错误时会出现console.log(),因此回调的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-- 我非常喜欢 Ben 使用 whilst() 发布的解决方案。这允许创建迭代连续运行的循环。欲了解更多信息,请查看 npm 页面here。

【讨论】:

【参考方案2】:

导致问题的不是 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);
              
            
          );

【讨论】:

【参考方案3】:

那里的代码有一些问题:

    回调被调用的地方。 res.redirect() 在哪里被调用。 for 循环。

save() 是异步的。常规 for 循环将继续进行,而无需等待所有 save() 调用完成。这就是destinationsArray 为空的原因。正如您所说,您不能使用 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'); 
);

【讨论】:

像魅力一样工作。完美的。万分感谢!不知道async.whilst

以上是关于for 循环内的 async.waterfall 转义 for 循环的主要内容,如果未能解决你的问题,请参考以下文章

Node.js 中 For 循环中的 async.waterfall

async.waterfall

如何等待来自 forEach 循环的多个异步调用?

nodejs 和 async.waterfall 带有 if 条件和条件函数列表。

nodejs 和 async.waterfall 带有 if 条件和条件函数列表。

async.waterfall 和 child_process.execSync 有啥区别?