我应该如何在许多嵌套的匿名函数中处理范围?

时间:2013-02-03 19:28:26

标签: javascript node.js mongodb

我在mongodb中有一个Collection,其中包含嵌入的引用文档,这迫使我运行多个anon函数。

首先我初始化这个数组

  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());
  });
});

现在问题是播放器阵列在第一次初始化时仍然保持原样。为什么播放器中的数据不存在?我知道为什么,因为它在另一个范围内,而不是在匿名函数内部调用。但是我应该如何将我的值添加到播放器数组呢?

整个片段。

// 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);

1 个答案:

答案 0 :(得分:0)

虽然Node.JS是单线程的,但它基于事件。做好工作,等待其他工作完成。等待MongoDB调用返回是一个可以“等待”的完美示例。在这种情况下,这意味着当Node进程正在等待响应时,它可以执行其他JavaScript代码。最终,MongoDB响应,代码在下一次“等待其他工作”机会时执行。

下面标记为#1,代码在playerCol.find返回之前执行。 Node.JS和MongoDB驱动程序是异步的。这意味着在初始调用之后,才会返回调用find的结果。稍后,当数据库响应值时,将进行回调。在第一次调用playerCol.find时,回调是在toArray中声明的内部函数。我已将其重命名为findPlayerAsync.

因此,在执行find之后,下一行运行的是console.log(player.deck)。如果您添加标记字符串after 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: [......]