【发布时间】:2015-10-22 00:44:03
【问题描述】:
我正在使用 MEANjs 进行一些工作以帮助获得一些经验,但我遇到了 Mongoose 的异步函数调用的问题。我不得不建立变通方法,因为我一心想以同步的心态进行编码。我做错了吗?
示例 1:在异步回调结束时调用下一个操作
例如,想象一个具有服务器端战斗代码的 JavaScript 游戏 (MEANjs)。此代码将抓取任何具有战斗命令(例如攻击、射击火球等)的 AI 敌方实体并处理这些命令。然而,首先需要做的是从 MongoDB 中查找可用能力列表,以便我们知道这些战斗命令需要做什么。例如,如果一个敌人想要执行他们的“Spit Fire IV”能力,返回的信息会让我们知道这会造成多少伤害,等等。
这需要调用 Mongoose 的 model.find,这是一个异步调用。回调将使用所有能力信息填充对象,这些信息可以稍后在战斗命令处理器中使用。 (这是麻烦的部分)。
一旦调用了 model.find 的回调,数据就会被存储,并且 processCombat() 函数将在回调结束时执行。没问题,对吧,这是最佳实践吗?我过去只是在调用这种类型的异步调用后放置一个 setTimeout 以确保填充对象,但这似乎是一个糟糕的设计。
示例 2:将数据传递给迭代调用的异步方法
想象一下,您现在处于这种战斗处理器方法中。您有一个 model.find 来获取必须处理的一组敌人,并且在该 find 的回调中,您必须执行 model.findOne 来获取该敌人的目标玩家并在其回调中执行某些事情,例如验证战斗命令,改变玩家的生命值,更新敌人让它知道它已经处理了它的战斗命令等等。
伪代码:对于每个准备战斗的敌人,1) 获取敌人,2) 获取它在战斗中瞄准的玩家,3) 更新玩家和敌人数据以完成战斗,即玩家失去一些生命值.
Enemy.find(... function(err,enemies) {
var enemyList = [];
for(var i=0; i < enemies.Length; i++) {
enemyList.push({
playerID: enemies[i].combatTargetID,
enemy: enemies[i],
processed: false
});
Player.findOne({_id: enemies[i].combatTargtID}, function(err,player) {
var enemy = null;
for(var j = 0; j < enemyList.length; j++) {
if(player.id === enemyList[j].playerID && enemyList[j].processed === false) {
enemy = enemyList[j].enemy;
enemyList[j].processed = true;
break;
}
}
//do things with enemy and player!
});
}
});
重要的部分是填充enemyList,然后在异步回调中使用它。很有可能,因为 findOne 是异步的,所以在第一个 findOne 回调执行时,enemyList 将被完全填充,但尽管如此,它不需要完全填充就可以有效地执行(即在回调将在执行 findOne 时可用)。一旦 findOne 被执行,它会遍历敌人列表以找到它自己的当前尚未处理的 playerID - 请记住,多个敌人可以针对同一个玩家,因此除了需要一个已处理的变量之外只需查找 playerID。
有没有更好的办法?
对此有一个警告:如果同时调用多个回调会怎样。可以创建多个实例在同一行上的竞争条件,因此使用处理后的变量不会 100% 完美..
(对不起,如果这是一个疯狂的问题,但这种类型的编程很奇怪,我觉得我是在针对它而不是在设计它,所以我正在寻找见解)
【问题讨论】:
-
啊废话哈哈,太早了。每次我说同步时,我的意思是异步。
-
不用担心。 :) 您几乎有两个选择,使用承诺,或带有回调的计数器,您更喜欢哪一个?我倾向于更倾向于 Promise,因为使用 Promise 处理错误通常更容易。
-
好吧,把这些词都翻了。希望它现在更有意义
-
@KevinB promises/callbacks 是很好的解决方案,但我主要关心的是示例 2,其中数据基本上需要传入并由回调使用
标签: javascript node.js multithreading callback mongoose