在 Node.js 的异步函数中填充数组

Posted

技术标签:

【中文标题】在 Node.js 的异步函数中填充数组【英文标题】:Populate an array within an asynchronous function in Node.js 【发布时间】:2015-10-02 03:33:55 【问题描述】:

我最近开始使用 Mongoose 和 Node.js,但在处理异步 Mongoose 查询的结果时遇到了一些困难。我有一个名为“组”的集合,一个组可以容纳位于一个名为“成员”的单独集合中的成员。为了实现这一点,每个组都有一个名为“members”的字段,它是一个 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
);

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

【问题讨论】:

一句话:Promises 不可能。 getProfile 永远不会返回您正在寻找的值。你需要做一些回调链或承诺工作。 出于好奇,为什么不能选择async? IMO 是迄今为止最干净的解决方案 @Plato:因为我想尽量减少外部依赖的使用,我知道可以通过使用回调链来解决。 【参考方案1】:

您可以利用 mongoose 方法返回 Promises 的事实。

注意:您需要使用 Node >= v0.12 或在模块中有一个 Promise 库 required。

// 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);
    );

【讨论】:

感谢您的回答。您在此处发布的代码运行良好。我想知道您是否也知道使用链式回调的解决方案? 是的,但是这种解决方案不太理想!如果这可行,那么它是一种比使用回调更简洁的解决方案,并且避免了您自己所说的“回调地狱”。 感谢您的回复。正如我已经提到的@Plato,我将继续使用您提供的这个解决方案:)。【参考方案2】:

我通过在没有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
  )
)

【讨论】:

感谢您的回答。我感谢您使用异步库的解决方案。由于我使用的是 Node 0.12.4,我将继续使用 Promise 解决方案。

以上是关于在 Node.js 的异步函数中填充数组的主要内容,如果未能解决你的问题,请参考以下文章

Node.js如何将数组索引传递给异步函数

如何使用 promise 将数组从我的模块返回到我的 API?异步函数和承诺 - Node.js

Node.js 中的异步函数

在 mongoose 中填充嵌套数组 - Node.js

当循环中调用了异步函数时,Node.JS 将如何处理循环控制?

无法在 node.js 中用猫鼬填充数组