NodeJs, javascript: .forEach 似乎是异步的?需要同步
Posted
技术标签:
【中文标题】NodeJs, javascript: .forEach 似乎是异步的?需要同步【英文标题】:NodeJs, javascript: .forEach seems to be asynchronous? need synchronization 【发布时间】:2012-05-21 16:57:45 【问题描述】:我目前正在与 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 循环完成之前发生的方式(这给了我一个错误的在线用户列表)?
非常感谢!
【问题讨论】:
【参考方案1】:以下控制台日志:
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
【讨论】:
谢谢!!这行得通,但是这种在 javascript 中的编程是否被视为“坏习惯”?或者以这种方式工作是否完全合法? 这不是一个坏习惯,这就是 javascript 的工作方式,你只需要习惯回调。无论如何,这显然不是最干净的方式,你可以构建自己的函数,包装函数,或使用类似:github.com/coolaj86/futures/tree/v2.0/forEachAsync,这也保证了函数回调的顺序(我提供的代码没有) 该解决方案有效,但大多数开发人员会使用 async.JS 或 Promise 库 (github.com/kriskowal/q)。其他库可以为您的代码提供更多“杠杆”。【参考方案2】:我能够通过将异步包添加到我的项目并将 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
);
);
);
【讨论】:
这是采用前进的最佳解决方案,无需担心变量或命令回调触发。谢谢!【参考方案3】:通过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
in vim)缩进整个文件。
但是,OnlineUser.findOne()
很可能是异步的。因此,即使您将呼叫移动到正确的位置,它也不起作用。请参阅ShadowCloud's answer 了解如何解决此问题。
【讨论】:
因为我一直在测试解决方案,我已经尝试将代码广告放在循环的末尾,没有任何区别 为什么不把res.render(...)
调用放在 foreach 的末尾,但放在第一个回调中。比重定向将在 foreach 完成后执行。
@graydsl 我不知道您的确切意思,但我想我尝试了大多数地方,它在循环之前一直在进行渲染:o
@Jeroen 忘记我说的话。我错了,因为OnlineUser.findOne(...)
电话。嗯..如果 ShadowCload 的答案对你有用,那很好。 :) 您可以阅读有关fibers 的信息,这将使您能够以同步方式使用异步函数。但它们并不是那么容易使用。 ;)以上是关于NodeJs, javascript: .forEach 似乎是异步的?需要同步的主要内容,如果未能解决你的问题,请参考以下文章
asp是什么, javascript和php,asp区别,什么是 JavaScript 引擎, nodejs和vuejs的关系,nodejs和javascript区别