在 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 库 require
d。
// 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 的异步函数中填充数组的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 promise 将数组从我的模块返回到我的 API?异步函数和承诺 - Node.js