NodeJs, javascript:.forEach кажется асинхронным? нужна синхронизация
В настоящее время я работаю над проектом с тремя друзьями, использующими nodeJs, expressJs, MongoDB, html5,...
Поскольку мы довольно новичок в этих технологиях, мы столкнулись с некоторыми проблемами.
Большой проблемой, с которой я не могу найти решение, является асинхронное выполнение определенного кода.
Я хочу, чтобы для каждого цикла был закончен, так что у меня есть обновленный список друзей в Интернете, а затем выполнить 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 асинхронный?), ответ заключается в том, что для каждого блокируется, но в мой пример кажется неблокирующим, потому что он выполняет res.render, прежде чем он завершит цикл?
Как я могу убедиться, что для каждого из них закончен, поэтому у меня есть обновленный список друзей (и список друзей), который я могу передать, вместо передачи 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
}
Ответ 2
Мне удалось решить что-то подобное, добавив пакет 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
});
});
});
}
Ответ 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
в vim).
Однако OnlineUser.findOne()
скорее всего асинхронен. поэтому даже если вы переместите звонок в нужное место, это не сработает. См. ответ ShadowCloud о том, как решить эту проблему.