NodeJs,javascript:.forEach似乎是异步的?需要同步吗

NodeJs,javascript:.forEach似乎是异步的?需要同步吗,javascript,node.js,asynchronous,foreach,synchronous,Javascript,Node.js,Asynchronous,Foreach,Synchronous,我目前正在与3个朋友一起使用nodeJs、expressJs、MongoDB、html5等进行一个项目,。。。 由于我们对这些技术相当陌生,因此我们遇到了一些问题。 我找不到解决方案的一个大问题是某些代码的异步执行 我想要一个for each循环完成,这样我就有了一个更新的在线好友列表,然后执行res.render(在其中我传递在线好友列表),因为当前它在完成循环之前执行res.render。 代码: } 输出如下: online friends: mark redirecting a loop

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

我想要一个for each循环完成,这样我就有了一个更新的在线好友列表,然后执行res.render(在其中我传递在线好友列表),因为当前它在完成循环之前执行res.render。 代码:

}

输出如下:

online friends: mark
redirecting
a loop
a loop
a loop
a loop
a loop
a loop
a loop
正如这里所讨论的(),答案是for-each是阻塞的,但在我的示例中,它似乎是非阻塞的,因为它在完成循环之前执行res.render? 如何确保for-each完成,以便我有一个最新的在线好友列表(和好友列表),在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
            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


但是,
OnlineUser.findOne()
很可能是异步的。因此,即使您将呼叫移动到正确的位置,它也无法工作。请参阅如何解决此问题。

以下控制台日志:

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
}

通过向项目中添加异步包并将forEach()更改为,我可以解决类似的问题。其优点是,这为应用程序的其他部分提供了一种标准的同步方法

类似于您的项目:

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
      });
    });
  });
}

由于我一直在测试一个解决方案,我已经尝试将代码放在循环的末尾,没有任何区别。为什么不将
res.render(…)
调用放在foreach结束后的第一个回调内呢。在foreach完成后,重定向将被执行。@graydsl我不知道你的确切意思,但我想我在大多数地方都试过了,它在循环之前一直在进行渲染:o@Jeroen忘了我说的吧。我错了,因为调用了
OnlineUser.findOne(…)
。陛下如果ShadowCload的答案对你有用,那就好了。:)您可以阅读,这将使您能够以同步方式使用异步函数。但它们不是那么容易使用谢谢这是可行的,但是这种javascript编程被认为是一种“糟糕的做法”吗?或者这样做完全合法吗?这不是一个坏习惯,这是javascript的工作方式,你只需要习惯回调。。无论如何,这显然不是最干净的方法,您可以构建自己的函数,包装函数,或者使用类似于:,这也保证了函数回调的顺序(我提供的代码没有)。解决方案可以工作,但大多数开发人员会使用async.JS或promise库()。其他库可以在代码中为您提供更多的“杠杆作用”。这是采用向前推进的最佳解决方案,无需担心变量,也无需对回调进行排序。谢谢
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
      });
    });
  });
}