2014-05-19 38 views
0
Scenario: There are users and users has many posts. For a particular group of users, I need to fetch 10 recent posts per user and send them in response. 
Here is what I have come up with: 

用户是具有用户信息的数组。保留嵌套for循环中的执行顺序和NodeJS中的回调MongoDB

var allPosts = []; 

for(var i=0; i<users.length; i++){ 
    (function(i){            //Level-1 
     db.collection('posts', function(err, postCollection){ 
      (function(i){          //Level-2 
       postCollection.find({"user_id": users[i]['user_id']}).sort({"created": -1}).limit(10).toArray(function(err, post) { 
        (function(i){        //Level-3 
         for(var j =0; j< post.length; j++){ 
          (function(j){ 
           post[j]['created'] = ObjectId(post[j]['_id'].toString()).getTimestamp(); 
           allPosts.push(post[j]); 
           if(j === post.length-1){ 
            res.send(allPosts); 
           } 
          })(j); 
         }  
        })(i); 
       }); 
      })(i); 
     });  
    })(i);       
} 

现在,执行顺序最多保留到Level-2,但是当它进入3级,所有的事情就出了问题:我有两个用户在阵列和一个用户有3个职位,另外有10个帖子,有时回复只有3个帖子,有时候是13个帖子。我认为它是因为MongoDB。我甚至通过使用立即调用的函数表达式(IIFE)来照顾执行顺序,但它似乎并没有在这里工作。 任何帮助表示赞赏。 谢谢

回答

1

首先,你应该美化你的代码。 在其他循环的回调中使用匿名函数并不容易维护或读取。

你的代码的问题是,在最后一个循环(j循环)中,你得到j == users.length - 1之前,其他用户的查询已完成,因此响应与发送的查询数一起发送直到那一刻。

您做出的另一个重大错误是您在循环内请求集合这是错的!您应该缓存数据库和集合。

试试这个代码:

var allPosts = []; 
var post_collection = null; 

var get_user = function(i, callback) { 
    post_collection 
     .find({"user_id": users[i]['user_id']}) 
     .sort({"created": -1}) 
     .limit(10) 
     .toArray(function(err, post) { 

      // Do something when you get error 
      // Always call the callback function if there is one 
      if(err) { 
       callback(); 
       return; 
      } 

      for(var j=0; j<post.length; ++j) { 
       post[j]['created'] = ObjectId(post[j]['_id'].toString()).getTimestamp(); 
       allPosts.push(post[j]); 
      } 

      callback(); 
     }); 
}; 

var fetch_users = function() { 
    var count = users.length; 

    for(var i=0; i<users.length; ++i) { 
     get_user(i, function() { 
      // Each time a query for one user is done we decrement the counter 
      count--; 

      // When the counter is 0 we know that all queries have been done 
      if(count === 0) { 
       res.send(allPosts); 
      } 
     }); 
    };  
}; 

// Get the collection, check for errors and then cache it! 
db.collection('posts', function(err, postCollection) { 

    // Always check for database errors 
    if(err) { 
     console.log(err); 
     return; 
    } 

    post_collection = postCollection; 
    fetch_users(); 
}); 

你应该知道,这个代码不进行测试。我可能错过了一个分号或一些大括号,但你应该很容易理解。

+0

谢谢@Catalin,它工作得很好。稍作修改:需要在调用get_user中的回调之前检查if(j === post.length-1){callback();}。此外,感谢您的建议,并帮助我在如此短的时间内 –

+0

是的你是对的,但你不需要if语句。实际上,回调应该在循环之后。我编辑了答案。您还应该在代码中更改它。 –

+0

是的,它也可以在循环外工作,再次感谢:) –