1

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

首先我初始化这个数组

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

1 回答 1

0

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

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

因此,在执行 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: [......]
于 2013-02-03T20:07:59.143 回答