在Node.js

时间:2015-07-13 18:22:23

标签: javascript node.js mongodb asynchronous

我最近开始使用Mongoose和Node.js,我在处理异步Mongoose查询的结果时遇到了一些困难。我有一个名为' Groups'并且一个小组可以容纳位于名为“会员”的单独集合中的成员。为了实现这一目标,每个小组都有一个名为“'成员”的字段。这是ObjectId的一个数组。此外,每个成员都有一个由ObjectId引用的配置文件。因此,每个成员都有一个名为' profile'并且配置文件存储在名为' Profiles'。

的集合中

现在我想获得给定组中的配置文件列表。因此我写了这段代码:

// Function that returns a profile by a given objectId
function getprofile(profileId, profile) {
  Profile
  .findById(profileId)
  .exec(function(err, res) {
    if(err) { return null; }
    if(!res) { return null; }
    return res;
  });
}

// Main function that returns a list of all (unique) profiles from members within a group
Group
.findById(req.params.id)
.populate('members')
.exec(function(err, group) {
  if(err) { return handleError(res, err); }

  var tmpProfiles = [];
  var allProfiles = [];
  for(var i=0; i<group.members.length; i++) {
    var memberProfile = group.members[i].profile;

    // Check if the member's profile is already in the array
    if(objectIndexOf(tmpProfiles, memberProfile) == -1) { 
        tmpProfiles.push(memberProfile);
        allProfiles.push(getprofile(memberProfile)); // add the profile to the array of profiles
    }
  }
  return res.json(allProfiles); // this returns an empty array
});

问题在于&#39; Profile.findById&#39;功能和&#39; Group.findById&#39;函数都是异步的,因此函数返回的数组只有&#39; null&#39;值。我知道我可以通过使用&#39; Async&#39;来解决这个问题。图书馆,但这不是我的选择,所以我必须经历地狱回调#39;。有人可以通过解决这个问题让我朝着正确的方向前进吗?我已经找到了一些使用递归调用函数的解决方案,但我不知道如何在这种情况下实现它。

2 个答案:

答案 0 :(得分:2)

你可以利用mongoose方法返回Promises这一事实。

注意:您需要使用Node&gt; = v0.12或在模块中使用Promise库require

// Function that returns a profile by a given objectId
function getprofile(profileId, profile) {
    return Profile.findById(profileId); // return the Promise of this `findById` call
}

// Main function that returns a list of all (unique) profiles from members within a group
Group
    .findById(req.params.id)
    .populate('members')
    .then(function(group) {
        var tmpProfiles = [];
        var promises = []; // store all promises from `getprofile` calls

        for(var i = 0; i < group.members.length; i++) {
            var memberProfile = group.members[i].profile;

            // Check if the member's profile is already in the array
            if(objectIndexOf(tmpProfiles, memberProfile) == -1) { 
                tmpProfiles.push(memberProfile);
                promises.push(getprofile(memberProfile)); 
            }
        }

        return Promise.all(promises);
    })
    .then(function(allProfiles) {
        res.json(allProfiles);
    })
    .catch(function(err) {
        //todo: handle error
        console.log(err);
    });

答案 1 :(得分:1)

我通过没有caolan/async的回调地狱来支持你的学习! 无论如何,这是一个异步的答案,与你的OP和承诺答案进行比较

Group...exec(function(err, group){
  async.waterfall([
    function(done1){
      var tmpUnique = [];
      async.filter(group.members, function(member, done2){
        var isUnique = (tmpUnique.indexOf(member.profile) === -1);
        if(isUnique){ tmpUnique.push(member.profile) };
        done2(isUnique);
      }, function(err, uniqueMembers){
        done1(err, uniqueMembers);
      })
    },
    function(uniqueMembers, done1){
      async.map(uniqueMembers, function(member, done2){
        getProfile(member.profile, done2);
        // rewrite getProfile(id, callback){} to callback(err, profile)
        // and maybe rename the key member.profile if its an id to get the real profile?
      }, function(err, uniqueProfiles){
        done1(err, uniqueProfiles)
      });
    },
  ], function(err, uniqueProfiles){
    //callback(err, uniqueProfiles) or do something further
  })
})