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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10579423/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 10:19:59  来源:igfitidea点击:

NodeJs, javascript: .forEach seems to be asynchronous? need synchronization

javascriptnode.jsasynchronousforeachsynchronous

提问by Jeroen

I am currently working on a project with 3 friends using nodeJs, expressJs, MongoDB, html5,... Since we're fairly new to these technologies we bumped into some problems. A big problem that I can't find a solution for is the asynchronous execution of certain code.

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

I want a for each loop to finish, so that I have an updated online friends list, and than execute the res.render (in which I pass the online friends list), because currently it does the res.render before it finishes the loop. Code:

我希望每个循环都完成,以便我有一个更新的在线好友列表,然后执行 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
});

}

}

output will be as follows:

输出如下:

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

As discussed here ( JavaScript, Node.js: is Array.forEach asynchronous?) , the answer is that the for-each is blocking, but in my example it seems to be non-blocking because it executes the res.render before it has finished looping? How can I make sure that the for each is finished so I have an up to date onlinefriends list (and friendlist) which I can than pass to the res.render instead of the res.render happening way before the for -each loop finishes (which gives me an incorrect list of online users) ?

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

Thanks very much!

非常感谢!

回答by BFil

The following console log:

以下控制台日志:

console.log("a loop");

is inside a callback

在回调中

I believe that the callback of the function OnlineUser.findOne()is called asynchronously, that is why the code will log "a loop" after the redirect log

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

You should put the redirection after all the loop callbacks have been executed

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

Something like:

就像是:

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
}

回答by unremarkable

I was able to solve something similar by adding the async package to my project and changing forEach() to async.each(). The advantage is that this provides a standard way to do synchronization for other parts of the application.

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

Something like this for your project:

你的项目是这样的:

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

回答by ThiefMaster

Running your code through jsbeautifierindents it properly and shows you why that happens:

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

So... always indent your code properly and you won't have issues like this. Some editors such as Vim can indent your whole file with a single shortcut (gg=Gin vim).

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

However, OnlineUser.findOne()is most likely asynchronous. so even if you move the call to the correct location it won't work. See ShadowCloud's answeron how to solve this.

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