如何在异步调用期间填充数组并将其发送到响应对象中

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 的解决方案是正确的,所以不要使用此代码。遇到这样的问题就记住了。