2012-05-14 49 views
14

我目前工作的一个项目有3个朋友使用的NodeJS,expressJs,MongoDB的,HTML5 ...... 因为我们是相当新的这些技术,我们遇到了一些问题。 ,我不能找到一个解决的一个大问题是某些代码的异步执行。的NodeJS,请javascript:.forEach似乎是异步的?需要同步

我想为每个循环完成一次,以便我有一个更新的在线朋友列表,并执行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?),答案是是,换每个阻塞,但在我的示例它似乎是非阻塞的,因为它在完成循环之前执行res.render? 我怎样才能确保每一个完成,所以我有一个最新的onlinefriends列表(好友列表和),我可以比传递到res.render代替res.render方式发生前的 - 每一个循环结束(这给了我一个不正确的在线用户列表)?

非常感谢!

回答

15

以下控制台日志:

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 
} 
+0

谢谢!!这是有效的,但是这种javascript编程被认为是“不好的做法”?还是完全合法的这样工作? – Jeroen

+1

这不是一个不好的做法,这是JavaScript的工作,你只需要如何去适应回调。不管怎么样,这显然是不干净的方法,你可以BUIL自己的功能,包装的功能,或者使用类似:HTTPS ://github.com/coolaj86/futures/tree/v2.0/forEachAsync,这也保证了函数回调的顺序(我提供的代码没有) – BFil

+0

谢谢你,这个工作完美的我:) – thtsigma

1

通过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's answer

+0

,因为我一直在测试的解决方案,我已经尝试把广告代码的循环结束,没有有所作为 – Jeroen

+0

你为什么不将** res.render(...)'call **放在foreach结尾后面的第一个回调中。比foreach结束后执行的重定向要多。 – jsbeckr

+0

@graydsl我不知道你的意思到底在哪里,但我想我尝试了大部分地方,它在循环之前一直在做渲染:o – Jeroen

6

我能够解决通过增加异步包到我的项目和改变的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 
     }); 
    }); 
    }); 
} 
+0

这是采用前进的最佳解决方案,无需担心变数或订购回调火灾。谢谢! – sidonaldson