nodejs:在for循环中保存函数,async麻烦

时间:2012-03-16 02:45:03

标签: node.js asynchronous

NodeJS + Express,MongoDB + Mongoose

我有一个JSON Feed,每个记录都有一组“场地”属性(比如“场地名称”,“场地位置”,“场地电话”等)。我想创建一个饲料中所有场地的集合 - 每个场地的一个实例,没有欺骗。

我遍历JSON并测试场地是否存在于我的场地集合中。如果没有,请保存。

jsonObj.events.forEach(function(element, index, array){
    Venue.findOne({'name': element.vname}, function(err,doc){
        if(doc == null){
            var instance = new Venue();
            instance.name = element.vname;
            instance.location = element.location;
            instance.phone = element.vphone;
            instance.save();
        }
    }
}

期望:所有场地的清单(没有欺骗)。

结果:场地集合中有很多骗局。

基本上,循环为JSON Feed中的每条记录创建了一个新的Venue记录。

我正在学习Node及其异步质量,所以我相信for循环在第一个save()函数完成之前就完成了 - 所以if语句总是检查空集合。 Console.logging支持这一说法。

我不确定如何重做这个以便它执行所需的任务。我尝试了caolan的异步模块,但我无法帮助它。我错误地使用了很多机会。

非常感谢你指出我正确的方向 - 我搜索无济于事。如果异步模块是正确的答案,我会非常乐意帮助您解决在这种特定情况下如何实现它。

再次感谢!

2 个答案:

答案 0 :(得分:2)

为什么不采用其他方式呢?你没有说你的持久层是什么,但它看起来像猫鼬或可能是FastLegS。在任何一种情况下,您都可以在“名称”字段上创建唯一索引。然后,您可以尝试保存任何内容,并在错误是唯一索引时处理错误。

答案 1 :(得分:1)

无论你做什么,都必须像@Paul建议的那样做,并在数据库中创建一个唯一的索引。这是确保唯一性的唯一方法。

但是代码的主要问题是在instance.save()调用中,需要一个触发下一次迭代的回调,否则数据库将没有时间保存新记录。这是竞争条件。你可以用caolan的forEachSeries函数解决这个问题。

或者,您可以获取Venue集合中已与JSON对象中的项匹配的记录数组,然后从对象中过滤匹配项,然后迭代地添加过滤后的JSON对象中剩余的每个项目。这将通过不首先尝试创建重复项来最小化数据库操作的数量。

Venue.find({'name': { $in: jsonObj.events.map(function(event){ return event.vname; }) }}, function (err, docs){
  var existingVnames = docs.map(function(doc){ return doc.name; });
  var filteredEvents = jsonObj.events.filter(function(event){
    return existingVnames.indexOf(event.vname) === -1;
  });
  filteredEvents.forEach(function(event){
    var venue = new Venue();
    venue.name = event.vname;
    venue.location = event.location;
    venue.phone = event.vphone;
    venue.save(function (err){
      // Optionally, do some logging here, perhaps.
      if (err) return console.error('Something went wrong!');
      else return console.log('Successfully created new venue %s', venue.name);
    });
  });
});