如何在异步调用期间填充数组并将其发送到响应对象中
How can I populate an array during an async call and send it in a response object
这是我正在尝试做的一个例子,检索 mongo 数据库中的所有 post,为每个 post 填充作者然后使用作者对象从 Cloudinary 检索个人资料图片。
执行此操作的正确方法是什么?我已经尝试了多种填充数组并在响应中发送数组的方法,但是由于异步调用,它们在发送 res 之前永远不会 运行 。
router.get('/posts',auth, function(req, res, next) {
//var id = req.payload._id;
Post.find(function(err, posts){
if(err){ return next(err); }
posts.forEach(function(post){
post.populate('author',function(err,post){
post.image = cloudinary.image("v"+post.author.avatarVersion+"/profile/"+post.author._id,{
width:100, height:100,crop:'thumb',gravity:'face',radius:'max'
})
//here the post object is updated
console.log(post)
})
})
//res.json(some posts array);
});
});
感谢 Dan Moldovan 的解决方案!
router.get('/posts',auth, function(req, res, next) {
var id = req.payload._id;
Post.find({}).populate('author').exec(function(err,posts){
if(err){ return next(err); }
var updatedPosts = [];
posts.forEach(function(post){
post.image = cloudinary.image("v"+post.author.avatarVersion+"/profile/"+post.author._id,{
width:100, height:100,crop:'thumb',gravity:'face',radius:'max'
})
updatedPosts.push(post);
})
res.json(updatedPosts);
})
您可以将 populate
查询链接到第一个查询,然后进行最终回调,例如
Post.find({}).populate('author').exec(function (err, posts) {
if(err){ return next(err); }
posts.forEach(function(post){
// here each post will already have author appended
});
});
Dan 的解决方案是正确的,但我想解释一下您遇到的问题。因为 post.populate()
是数据库调用,意味着代码是异步的。这意味着 forEach()
中的下一个 post 将在 .populate()
与前一个 post 完成之前开始 运行。这意味着并非所有 post 都会在 res.json()
执行之前完成。一个解决方案(在这种情况下不需要,但您当前的代码可以使用)是使用 async library:
var async = require("async");
router.get('/posts',auth, function(req, res, next) {
var id = req.payload._id;
Post.find(function (err, posts) {
if (err) {
return next(err);
}
// This is a forEach that waits until all posts are populated before moving on
async.each(posts, function (currentPost, postCallback) {
currentPost.populate("author", function (err, post) {
if (err) {
return postCallback(err);
}
post.image = cloudinary.image("v" + post.author.avatarVersion + "/profile/" + post.author._id, {
width: 100, height: 100, crop: 'thumb', gravity: 'face', radius: 'max'
});
// the callback function for the current post
postCallback();
});
}, function (error) {
// the final callback function once all postCallback()'s have been executed, or an error
if (error) {
return next(error);
}
// here, everything is finished
res.json(posts);
});
});
});
同样,Dan 的解决方案是正确的,所以不要使用此代码。遇到这样的问题就记住了。
这是我正在尝试做的一个例子,检索 mongo 数据库中的所有 post,为每个 post 填充作者然后使用作者对象从 Cloudinary 检索个人资料图片。
执行此操作的正确方法是什么?我已经尝试了多种填充数组并在响应中发送数组的方法,但是由于异步调用,它们在发送 res 之前永远不会 运行 。
router.get('/posts',auth, function(req, res, next) {
//var id = req.payload._id;
Post.find(function(err, posts){
if(err){ return next(err); }
posts.forEach(function(post){
post.populate('author',function(err,post){
post.image = cloudinary.image("v"+post.author.avatarVersion+"/profile/"+post.author._id,{
width:100, height:100,crop:'thumb',gravity:'face',radius:'max'
})
//here the post object is updated
console.log(post)
})
})
//res.json(some posts array);
});
});
感谢 Dan Moldovan 的解决方案!
router.get('/posts',auth, function(req, res, next) {
var id = req.payload._id;
Post.find({}).populate('author').exec(function(err,posts){
if(err){ return next(err); }
var updatedPosts = [];
posts.forEach(function(post){
post.image = cloudinary.image("v"+post.author.avatarVersion+"/profile/"+post.author._id,{
width:100, height:100,crop:'thumb',gravity:'face',radius:'max'
})
updatedPosts.push(post);
})
res.json(updatedPosts);
})
您可以将 populate
查询链接到第一个查询,然后进行最终回调,例如
Post.find({}).populate('author').exec(function (err, posts) {
if(err){ return next(err); }
posts.forEach(function(post){
// here each post will already have author appended
});
});
Dan 的解决方案是正确的,但我想解释一下您遇到的问题。因为 post.populate()
是数据库调用,意味着代码是异步的。这意味着 forEach()
中的下一个 post 将在 .populate()
与前一个 post 完成之前开始 运行。这意味着并非所有 post 都会在 res.json()
执行之前完成。一个解决方案(在这种情况下不需要,但您当前的代码可以使用)是使用 async library:
var async = require("async");
router.get('/posts',auth, function(req, res, next) {
var id = req.payload._id;
Post.find(function (err, posts) {
if (err) {
return next(err);
}
// This is a forEach that waits until all posts are populated before moving on
async.each(posts, function (currentPost, postCallback) {
currentPost.populate("author", function (err, post) {
if (err) {
return postCallback(err);
}
post.image = cloudinary.image("v" + post.author.avatarVersion + "/profile/" + post.author._id, {
width: 100, height: 100, crop: 'thumb', gravity: 'face', radius: 'max'
});
// the callback function for the current post
postCallback();
});
}, function (error) {
// the final callback function once all postCallback()'s have been executed, or an error
if (error) {
return next(error);
}
// here, everything is finished
res.json(posts);
});
});
});
同样,Dan 的解决方案是正确的,所以不要使用此代码。遇到这样的问题就记住了。