Javascript 我应该如何处理许多嵌套匿名函数中的作用域?
我在mongodb中有一个集合,其中包含嵌入的引用文档,这迫使我运行多个anon函数 首先我初始化这个数组Javascript 我应该如何处理许多嵌套匿名函数中的作用域?,javascript,node.js,mongodb,Javascript,Node.js,Mongodb,我在mongodb中有一个集合,其中包含嵌入的引用文档,这迫使我运行多个anon函数 首先我初始化这个数组 var player = { name : '', life : 10, gold : 50, score : 0, clientId : socket.id, playerId : data.playerId, deck : [] }; 现在我需要通过从mongodb获取数据来向数据组添加数据 // First find
var player = {
name : '',
life : 10,
gold : 50,
score : 0,
clientId : socket.id,
playerId : data.playerId,
deck : []
};
现在我需要通过从mongodb获取数据来向数据组添加数据
// First find the player.
var playerCol = db.collection('Guest');
playerCol.find({'_id' : new ObjectId(data.playerId)}).toArray(function(err, playerRes) {
// Traverse and load each tower in deck.
// Traverse each embedded reference, so each can be fetched.
playerRes[0].deck.towers.forEach(function(tower) {
// fetch data for tower.
towerCol = db.collection('Tower');
towerCol.find({'_id' : new ObjectId(tower.oid)}).toArray(function(err, completeTower) {
// Add the tower data to the deck.
player.deck.push(completeTower.pop());
});
});
现在的问题是,播放器数组仍然保持最初初始化时的状态。为什么我在player中的数据不存在?我知道为什么,因为它不在匿名函数中调用时在另一个作用域中。但是我应该如何将我的值添加到播放器数组中呢
这些片段是完整的
// Initialize the player.
var player = {
name : '',
life : 10,
gold : 50,
score : 0,
clientId : socket.id,
playerId : data.playerId,
deck : []
};
// Load players deck.
var playerCol = db.collection('Guest');
playerCol.find({'_id' : new ObjectId(data.playerId)}).toArray(function(err, playerRes) {
// Traverse and load each tower in deck.
playerRes[0].deck.towers.forEach(function(tower) {
towerCol = db.collection('Tower');
towerCol.find({'_id' : new ObjectId(tower.oid)}).toArray(function(err, completeTower) {
player.name = "Melvar";
player.deck.push(completeTower.pop());
console.log(player.deck); // Prints the data, just pushed
});
});
console.log(player.deck); // Prints empty array.
process.exit(1);
虽然Node.JS是单线程的,但它基于事件。完成工作并等待其他工作完成。等待MongoDB调用返回就是一个可以等待的完美例子。在本例中,这意味着当节点进程等待响应时,它可以执行其他JavaScript代码。最后,MongoDB做出响应,并在下一次等待其他工作机会时执行代码 下面标记为1,代码在playerCol.find返回之前执行。Node.JS和MongoDB驱动程序是异步的。这意味着调用find的结果直到初始调用完成后才会返回。稍后,当数据库用这些值响应时,将进行回调。在对playerCol.find的第一次调用中,回调函数是在toArray中声明的内部函数。我已将其重命名为findPlayerAsync 因此,在执行查找之后,下一行就是console.logplayer.deck。如果在find:之后添加一个标记字符串,您将看到该值在另一个console.log执行标记为2之前显示在控制台上
// Load players deck.
var playerCol = db.collection('Guest');
playerCol.find({'_id' : new ObjectId(data.playerId)}).toArray(function findPlayerAsync(err, playerRes) {
// Traverse and load each tower in deck.
playerRes[0].deck.towers.forEach(function(tower) {
towerCol = db.collection('Tower');
towerCol.find({'_id' : new ObjectId(tower.oid)}).toArray(function findTowerAsync(err, completeTower) {
player.name = "Melvar";
player.deck.push(completeTower.pop());
console.log('Inside towerCol.find: ' + player.deck); // #2 Prints the data, just pushed
});
});
console.log('After find: ' + player.deck); // #1 Prints empty array.
process.exit(1);
我希望控制台是:
After find: []
Inside towerCol.find: [......]
闭包和数据看起来没问题,我看不出您在嵌套范围内重新声明player变量。您在哪里检查播放器数组的值?它需要在towerCall调用完成之后。toArray在push调用之后完成。但是deck是,如果在push之后执行console.logplayer,则值确实存在。@martin yes在我看到您的comment@WiredPrairie如果我在推送之后将player对象记录为console.log,则该值存在。但是,如果我将console.log移出playerCol.find函数,数据就会消失。如果这不起作用,您需要发布更多的代码:值的console.log唯一起作用的地方是tower.find调用的末尾。因为它都是异步的,这就是为什么会有回调,所以您需要等到所有的工作都完成了。如果您只是在playerCol.find函数之后记录值,它将始终为空,因为其他代码还没有返回。谢谢WiredPairie,这是非常详细的解释。虽然这很有道理,但我仍在为我应该如何处理它而挣扎。我可以强制它以任何方式等待数据库请求吗?或者有没有一种方法可以不使用异步回调来执行mongodb查询?您可能希望查看类似于for node的库。