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
NodeJs, javascript: .forEach seems to be asynchronous? need synchronization
提问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=G
in 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 的回答。