简体   繁体   English

我应该如何处理许多嵌套匿名函数中的作用域?

[英]How should i handle scope within many nested anonymous functions?

I have a Collection in mongodb, which contains embedded referenced documents, which forces me to run thought multiple anon functions. 我在mongodb中有一个Collection,其中包含嵌入式引用的文档,这迫使我运行多个anon函数。

First i initialize this array 首先我初始化这个数组

  var player = {
    name : '',
    life : 10,
    gold : 50,
    score : 0,
    clientId : socket.id,
    playerId : data.playerId,
    deck : []
  };

Now i need to add data to the deck, by fetching data from mongodb. 现在,我需要通过从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());
  });
});

Now the problem is that the player array is still as it was, when it was first initialized. 现在的问题是,播放器数组在首次初始化时仍保持原样。 Why doesn't my data in player persist? 为什么播放器中的数据无法保存? Well i know why, because it's in another scope when not called inside a anonymous function. 我知道为什么,因为当不在匿名函数中调用时,它在另一个范围内。 But how should i approach adding my values to the player array? 但是我应该如何将我的值添加到播放器数组中?

The snippets in one piece. 片段合为一体。

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

While Node.JS is single threaded, it's based on events. Node.JS是单线程的,但它基于事件。 Do work and wait for other work to complete. 做工作,等待其他工作完成。 Waiting for a MongoDB call to return is a perfect example of something that can be "waited" upon. 等待MongoDB调用返回是可以“等待”的完美示例。 In this case, it means that while the Node process is waiting for a response, it can execute other JavaScript code. 在这种情况下,这意味着在Node进程等待响应时,它可以执行其他JavaScript代码。 Eventually, MongoDB responds, and the code is executed at the next "wait for other work" opportunity. 最终,MongoDB做出响应,并在下一次“等待其他工作”机会时执行代码。

Marked as #1 below, the code executes before the playerCol.find returns. 在下面标记为#1的代码在playerCol.find返回之前执行。 Node.JS and the MongoDB drivers are asynchronous. Node.JS和MongoDB驱动程序是异步的。 This means that the results of calling find aren't returned until after the initial call is made. 这意味着直到进行首次调用 ,才返回调用find的结果。 Later, when the database has responded with the values, the callback is made. 稍后,当数据库用值响应时,将进行回调。 In the first call to playerCol.find , the callback is the inner function declared with in toArray. 在对playerCol.find的首次调用中,回调是在toArray中声明的内部函数。 I've renamed it to findPlayerAsync. 我已将其重命名为findPlayerAsync.

So, right after the find is executed, the next line that runs is the console.log(player.deck) . 因此,在执行查找之后,下一行将运行console.log(player.deck) If you add a marker string as shown after find: , you'll see that value is displayed on the console before the other console.log executes (marked as #2). 如果after find:添加标记字符串,则会在另一个console.log执行之前将其显示在控制台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);

I'd expect the console to be: 我希望控制台是:

After find: []
Inside towerCol.find: [......]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM