14

我目前正在与 3 个朋友一起使用 nodeJs、expressJs、MongoDB、html5 进行一个项目……由于我们对这些技术还很陌生,因此遇到了一些问题。我找不到解决方案的一个大问题是某些代码的异步执行。

我想要一个 for each 循环完成,以便我有一个更新的在线朋友列表,然后执行 res.render (我在其中传递在线朋友列表),因为目前它在完成循环之前执行 res.render . 代码:

function onlineFriends(req, res) {
var onlinefriends = new Array();
onlinefriends.push("mark");
FriendList.findOne({
    owner: req.session.username
}, function (err, friendlist) {
    friendlist.friends.forEach(function (friend) { // here forEach starts
        OnlineUser.findOne({
            userName: friend
        }, function (err, onlineFriend) {
            if (onlineFriend != null) {
                onlinefriends.push(onlineFriend.userName);
                console.log("a loop");
            }
        });

    });  
        console.log("online friends: " + onlinefriends);
        console.log("redirecting");
        res.render('index', { // this is still inside the forEach function
            friendlist: friendlist.friends,
            onlinefriendlist: onlinefriends,
            username: req.session.username
        });// and here it ends
});

}

输出如下:

online friends: mark
redirecting
a loop
a loop
a loop
a loop
a loop
a loop
a loop

正如这里所讨论的(JavaScript, Node.js: is Array.forEach asynchronous?),答案是 for-each 是阻塞的,但在我的示例中,它似乎是非阻塞的,因为它在执行 res.render 之前它有完成循环?如何确保 for each 已完成,以便我有一个最新的 onlinefriends 列表(和friendlist),然后我可以将其传递给 res.render 而不是 res.render 在 for -each 循环完成之前发生的方式(这给了我一个不正确的在线用户列表)?

非常感谢!

4

3 回答 3

15

以下控制台日志:

console.log("a loop");

在回调中

我相信函数OnlineUser.findOne()的回调是异步调用的,这就是为什么代码会在重定向日志之后记录“循环”

您应该在执行完所有循环回调后进行重定向

就像是:

var count = 0;
friendlist.friends.forEach(function (friend) { // here forEach starts
    OnlineUser.findOne({
        userName: friend
    }, function (err, onlineFriend) {
        count++;
        if (onlineFriend != null) {
            onlinefriends.push(onlineFriend.userName);
            console.log("a loop");
        }
        if(count == friendlist.friends.length) { // check if all callbacks have been called
            redirect();
        }
    });
}); 

function redirect() {
    console.log("online friends: " + onlinefriends);
    console.log("redirecting");
    res.render('index', { // this is still inside the forEach function
        friendlist: friendlist.friends,
        onlinefriendlist: onlinefriends,
            username: req.session.username
    });// and here it ends
}
于 2012-05-14T08:36:47.447 回答
6

通过将 async 包添加到我的项目并将 forEach() 更改为async.each(),我能够解决类似的问题。优点是这提供了一种为应用程序的其他部分进行同步的标准方法。

你的项目是这样的:

function onlineFriends(req, res) {
  var onlinefriends = new Array();
  onlinefriends.push("mark");

  FriendList.findOne({owner: req.session.username}, function (err, friendlist) {
    async.each(friendlist.friends, function(friend, callback) {
      OnlineUser.findOne({userName: friend}, function (err, onlineFriend) {
        if (onlineFriend != null) {
          onlinefriends.push(onlineFriend.userName);
          console.log("a loop");
        }
        callback();
      });
    }, function(err) {
      console.log("online friends: " + onlinefriends);
      console.log("redirecting");
      res.render('index', { // this is still inside the forEach function
          friendlist: friendlist.friends,
          onlinefriendlist: onlinefriends,
          username: req.session.username
      });
    });
  });
}
于 2014-03-31T02:38:37.953 回答
2

通过jsbeautifier运行您的代码可以正确缩进并告诉您为什么会发生这种情况:

function onlineFriends(req, res) {
    var onlinefriends = new Array();
    onlinefriends.push("mark");
    FriendList.findOne({
        owner: req.session.username
    }, function (err, friendlist) {
        friendlist.friends.forEach(function (friend) { // here forEach starts
            console.log("vriend: " + friend);
            OnlineUser.findOne({
                userName: friend
            }, function (err, onlineFriend) {
                if (onlineFriend != null) {
                    onlinefriends.push(onlineFriend.userName);
                    console.log("online friends: " + onlinefriends);
                }
            });
            console.log("nu door verwijzen");
            res.render('index', { // this is still inside the forEach function
                friendlist: friendlist.friends,
                onlinefriendlist: onlinefriends,
                username: req.session.username
            });
        });  // and here it ends
    });

所以......总是正确缩进你的代码,你不会有这样的问题。一些编辑器(例如 Vim)可以使用单个快捷方式(gg=G在 vim 中)缩进整个文件。

但是,OnlineUser.findOne()很可能是异步的。因此,即使您将呼叫移动到正确的位置,它也不会起作用。有关如何解决此问题,请参阅ShadowCloud 的答案。

于 2012-05-14T07:58:11.223 回答