异步函数在调用一次但不是多次的情况下有效

时间:2014-05-03 09:43:10

标签: javascript node.js async.js

我已经使用async.waterfall来压缩嵌套函数,如下所示。

function convertPortfolio(trademarks, fn){
    async.waterfall([function(callback){
        groupByCountry(trademarks, callback)
    },
    function(TMsGroupedByCountry, callback){
        addEUTrademarks(TMsGroupedByCountry['EU'], TMsGroupedByCountry, callback)
    },
    function(revisedGroupByCountry, callback){
        groupTrademarksByStatus(revisedGroupByCountry, callback)

    }], function(err, splitByStatus){
        fn(null, splitByStatus);
    })
}

嵌套替代

function convertPortfolio(trademarks, fn){
    groupByCountry(trademarks, function(err, TMsGroupedByCountry){
        addEUTrademarks(TMsGroupedByCountry['EU'], TMsGroupedByCountry,   function(err, revisedGroupByCountry){
            groupTrademarksByStatus(revisedGroupByCountry, function(err, splitByStatus){
                 fn(null, splitByStatus)
            });
        });
    });
 }

随后当我将此函数作为另一个函数的一部分调用时,它完美地运行。但是当我尝试使用来自阵列上的forEach调用的单独值多次调用该函数时,当嵌套版本工作正常时,它无法工作。我无法解释为什么会出现这种情况,并且说实话,我不知道从哪里开始。我的理解是forEach调用应该只是意味着相应地处理每个单独的值并关闭,所以这不应该是问题。

异步功能正常工作并在此实例中返回值

exports.convertPortfolioAndAddToGeoJSON = function(geojson, trademarks, fn){
   convertPortfolio(trademarks, function(err, revisedTMs){
    addToGeoJson(geojson, revisedTMs, function(err, gj){
        fn(null, gj)
      });
   });
}

但是在这种情况下,未填充结束目标对象:

exports.convertYearPortfolioAndAddToGeoJSON = function(geojson, trademarks, fn){
  var target = {};
  Object.keys(trademarks).forEach(function(year){
    convertPortfolio(trademarks[year], function(err, revisedTMs){
        addToGeoJson(geojson, revisedTMs, function(err, revisedGeoJSON){
            target[year] = revisedGeoJSON;
            });
        });
    });
    fn(null, target);
  }

在某些点使用console.log显示在嵌套示例中,在记录目标对象之前记录返回值,而使用async.waterfall示例时,在返回的数据可用之前记录目标对象(所以我认为记录目标导致空对象并不奇怪)。

我认为在每种情况下都存在回调,这意味着目标的记录只会在所有先前的处理完成后才会发生,但是虽然这似乎是嵌套替代的情况,但事实并非如此至少在多次调用时使用异步版本。

任何指导都将不胜感激。

更新出于兴趣,这里使用async.forEach修改了代码:

exports.convertYearPortfolioAndAddToGeoJSON = function(geojson, trademarks, fn){
  var target = {};
  async.forEach(Object.keys(trademarks), function(year, callback){
    async.waterfall([
        async.apply(convertPortfolio, trademarks[year]),
        function(revisedTMs, callback){
            addToGeoJson(geojson, revisedTMs, callback)
        }], 
        function(err, revisedGeoJSON){
            target[year] = revisedGeoJSON;
            callback()
        })
   }, function(err){
       fn(null, target);
   });
}

1 个答案:

答案 0 :(得分:1)

Object.keys(trademarks).forEach是同步的,无法正确跟踪闭包/范围。您需要在那里使用async.forEach(Object.keys(trademarks), function(year, callback)....,并相应地调整异步控制流。

当你使用这个带有微小包装函数的模式时,也就是FYI:

function convertPortfolio(trademarks, fn){
  async.waterfall([function(callback){
    groupByCountry(trademarks, callback)
  },

您可以使用async.apply作为该样板:

function convertPortfolio(trademarks, fn){
  async.waterfall([async.apply(groupByCountry, trademarks),
     ...