Nodejs中forEach中有个异步函数,如何等异步函数执行完毕之后再执行整个函数的callback

Nodejs中forEach中有个异步函数,如何等异步函数执行完毕之后再执行整个函数的callback

exports.getComments=function(query,skip,limit,callback){
Comment.find(query).sort({create_time:-1}).skip(skip).limit(limit).exec(function(error,comments){
if(error){
callback(error,null);
}
comments.foreach(function(index,comment){
Comment.find({reply_ID:comment.comment_ID}, function(err, replys){
comment.reply=replys;
});
});
callback(error,comments);
})

我的想法是将comment增加一个reply属性,然后再返回comments集合,但由于Comment.find是异步的,故向各位大虾求助,提供点解决方案的,初学,求照顾


10 回复

在Node.js中使用forEach循环处理异步操作时,由于forEach本身是同步的,而其中的异步函数(如Comment.find)是异步的,这会导致回调函数在所有异步操作完成之前就被调用。为了解决这个问题,我们可以使用一些方法来确保所有的异步操作都完成后再执行回调。

一种常见的方法是使用async库中的eachSeriesfor await (const item of array)(对于支持async/await的环境)。这里我将展示如何使用async库的eachSeries来解决这个问题。

首先,你需要安装async库:

npm install async

然后,你可以修改你的函数如下:

const async = require('async');

exports.getComments = function(query, skip, limit, callback) {
    Comment.find(query).sort({ create_time: -1 }).skip(skip).limit(limit).exec(function(error, comments) {
        if (error) {
            callback(error, null);
            return;
        }

        // 使用 async.eachSeries 来确保每个异步操作按顺序完成
        async.eachSeries(comments, function(comment, eachCallback) {
            Comment.find({ reply_ID: comment.comment_ID }, function(err, replys) {
                if (err) {
                    eachCallback(err); // 如果有错误,停止后续操作
                    return;
                }
                comment.reply = replys;
                eachCallback(); // 完成当前迭代
            });
        }, function(err) {
            // 所有异步操作完成后的回调
            if (err) {
                callback(err, null);
            } else {
                callback(null, comments);
            }
        });
    });
}

在这个例子中,我们使用了async.eachSeries来确保每个异步操作(即Comment.find)在继续下一个操作之前已经完成。eachSeries接受两个参数:一个数组和一个迭代函数。迭代函数接收当前元素、一个回调函数作为参数。当所有的异步操作完成时,最后一个参数是一个最终的回调函数,它会在所有异步操作完成后被调用。

这样可以确保所有的评论及其回复都被正确加载,并且只有在所有异步操作完成后才会调用外部的callback函数。


谢谢你的回答,但这样的话貌似会触发N次callback

我的解决思路:

 Comment.find(query).sort({create_time:-1}).skip(skip).limit(limit).exec(function(error,comments){
    if (comments.length === 0) {
        return callback(error, []);
    }
    var proxy = new EventProxy();
    var done = function () {
        return callback(error, comments);
    };
    proxy.after('reply', comments.length, done);
    comments.forEach(function(comment,index){
        Comment.find({reply_ID:comment.comment_ID},function(error,replys){
            comment.reply=replys;
            proxy.trigger('reply',replys);
        })
    })
});  

感觉应该可以吧

那你能不能说一下你这段代码到底是什意思?比如comment 是什么。 我们才能给你答案。没头没尾的贴出代码谁能看懂。

not tested, might work:

var ln = comments.length
comments.foreach(function(index, comment) {
     Comment.find({ reply_ID: comment.comment_ID}, function(err, replys) {
        comment.reply = replys;
        ln --
        if (ln === 0) {
           callback(null, comments);
        }
     });
  });'

这种方式我试过了,貌似不行,我用EventProxy解决了,参考了一下咱们论坛的源码

我这个就是一个评论,回复模块,就像咱们现在这个club一样,你评论就是comment,我再回复你就是reply,关联id就是reply_id=comment_id,评论和回复都放在了同一个表里面

为何不写在存储过程里

MongoDB的存储过程还真不清楚

在这个问题中,forEach循环中的异步操作会导致回调函数提前被调用,而没有等待所有异步操作完成。为了解决这个问题,可以使用 Promise.all 来等待所有的异步操作完成后再执行回调函数。

以下是一个示例代码,展示了如何使用 Promise.all 来实现这一点:

exports.getComments = function (query, skip, limit, callback) {
  Comment.find(query).sort({ create_time: -1 }).skip(skip).limit(limit).exec(function (error, comments) {
    if (error) {
      callback(error, null);
      return;
    }

    const promises = comments.map(comment => {
      return new Promise((resolve, reject) => {
        Comment.find({ reply_ID: comment.comment_ID }, function (err, replys) {
          if (err) {
            reject(err);
          } else {
            comment.reply = replys;
            resolve(comment);
          }
        });
      });
    });

    Promise.all(promises)
      .then(comments => {
        callback(null, comments);
      })
      .catch(error => {
        callback(error, null);
      });
  });
};

解释

  1. 查询评论:首先查询数据库获取评论列表。
  2. 创建 promise 数组:对于每个评论,创建一个 promise,该 promise 在异步查询回复后解析评论对象。
  3. 使用 Promise.all:等待所有 promise 完成,并将结果传递给回调函数。
  4. 处理错误:如果任何 promise 被拒绝(即发生错误),则立即调用回调函数并传递错误。

这样可以确保所有异步操作完成后再调用最终的回调函数。

回到顶部