1

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

1 に答える 1

0

Node.JS はシングル スレッドですが、イベントに基づいています。作業を行い、他の作業が完了するのを待ちます。MongoDB 呼び出しが返されるのを待つことは、「待機」できるものの完璧な例です。この場合、Node プロセスが応答を待っている間、他の JavaScript コードを実行できることを意味します。最終的に、MongoDB が応答し、次の「他の作業を待つ」機会でコードが実行されます。

以下の #1 としてマークされているコードは、playerCol.find戻る前に実行されます。Node.JS と MongoDB ドライバーは非同期です。これは、最初の呼び出しが行われるまでfind呼び出しの結果が返されないことを意味します。その後、データベースが値で応答したときに、コールバックが行われます。への最初の呼び出しでは、コールバックは in 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 に答える