异步 JavaScript 解决方法
Asynchronous JavaScript workarounds
我正在使用 MEANjs 进行一些工作以帮助获得一些使用经验,但我遇到了 Mongoose 的异步函数调用问题。我不得不建立变通办法,因为我一心想以同步的心态编码。我做错了吗?
示例 1:在异步回调结束时调用下一个操作
例如,想象一个具有服务器端战斗代码的 JavaScript 游戏 (MEANjs)。此代码将获取任何具有战斗命令(例如攻击、射击火球等)的 AI 敌方实体并处理这些命令。然而,需要做的第一件事是从 MongoDB 中查找可用能力列表,以便我们知道这些战斗命令需要做什么。例如,如果一个敌人想要执行他们的 "Spit Fire IV" 能力,返回的信息会让我们知道这会造成多少伤害等。
这需要调用 Mongoose 的 model.find,这是一个异步调用。回调将使用所有能力信息填充一个对象,这些信息稍后可以在战斗命令处理器中使用。 (这是麻烦的部分)。
一旦 model.find 的回调被调用,数据将被存储,并且 processCombat() 函数将在此回调结束时执行。这里没有问题,对吧,这是最佳实践吗?我过去只是在调用这种类型的异步调用后放置一个 setTimeout 以确保填充对象,但这似乎是一个糟糕的设计。
示例 2:将数据传递给迭代调用的异步方法
想象一下,现在您正处于这种战斗处理器方法中。您有一个 model.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 是异步的,所以 enemyList 将在第一个 findOne 回调执行时被完全填充,但尽管如此,它不需要被完全填充就可以有效地执行(即在中使用的目标对象)回调将在执行 findOne 时可用)。一旦 findOne 被执行,它会遍历敌人列表以找到它自己当前尚未处理的 playerID——请记住,多个敌人可以瞄准同一个玩家,因此除了只需查找 playerID。
有没有更好的方法?
对此的警告:如果同时调用多个回调怎么办。可以在同一行上创建多个实例的竞争条件,因此使用处理后的变量不会 100% 完美..
(很抱歉,如果这是一个疯狂的问题,但这种类型的编程很奇怪,我觉得我是在针对它而不是使用它进行设计,所以我正在寻找见解)
在这种情况下,我会将流程分解为 return 承诺的几个函数,然后将它们链接在一起。
function getEnemies (obj) {
return new Promise(function (resolve) {
Enemy.find(... function(err,enemies) {
if (err) {throw err;}
obj.enemies = enemies.map(function (enemy) {
return {
playerID: enemy.combatTargetID,
enemy: enemy,
processed: false
};
});
resolve(obj);
});
});
}
function getPlayers (obj) {
return Promise.all(obj.enemies.map(function (enemy) {
return new Promise(function (resolve) {
Player.findOne({_id: enemy.combatTargtID}, function(err,player) {
if (err) {throw err;}
enemy.player = player;
resolve();
});
})
}).then(function () {
return obj;
});
}
function doWork() {
getEnemies()
.then(getPlayers)
.then(function (obj) {
console.log(obj); // do stuff with enemies and players here
}).catch(function (err) {
console.log(err, err.stack);
});
}
我正在使用 MEANjs 进行一些工作以帮助获得一些使用经验,但我遇到了 Mongoose 的异步函数调用问题。我不得不建立变通办法,因为我一心想以同步的心态编码。我做错了吗?
示例 1:在异步回调结束时调用下一个操作
例如,想象一个具有服务器端战斗代码的 JavaScript 游戏 (MEANjs)。此代码将获取任何具有战斗命令(例如攻击、射击火球等)的 AI 敌方实体并处理这些命令。然而,需要做的第一件事是从 MongoDB 中查找可用能力列表,以便我们知道这些战斗命令需要做什么。例如,如果一个敌人想要执行他们的 "Spit Fire IV" 能力,返回的信息会让我们知道这会造成多少伤害等。
这需要调用 Mongoose 的 model.find,这是一个异步调用。回调将使用所有能力信息填充一个对象,这些信息稍后可以在战斗命令处理器中使用。 (这是麻烦的部分)。
一旦 model.find 的回调被调用,数据将被存储,并且 processCombat() 函数将在此回调结束时执行。这里没有问题,对吧,这是最佳实践吗?我过去只是在调用这种类型的异步调用后放置一个 setTimeout 以确保填充对象,但这似乎是一个糟糕的设计。
示例 2:将数据传递给迭代调用的异步方法
想象一下,现在您正处于这种战斗处理器方法中。您有一个 model.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 是异步的,所以 enemyList 将在第一个 findOne 回调执行时被完全填充,但尽管如此,它不需要被完全填充就可以有效地执行(即在中使用的目标对象)回调将在执行 findOne 时可用)。一旦 findOne 被执行,它会遍历敌人列表以找到它自己当前尚未处理的 playerID——请记住,多个敌人可以瞄准同一个玩家,因此除了只需查找 playerID。
有没有更好的方法?
对此的警告:如果同时调用多个回调怎么办。可以在同一行上创建多个实例的竞争条件,因此使用处理后的变量不会 100% 完美..
(很抱歉,如果这是一个疯狂的问题,但这种类型的编程很奇怪,我觉得我是在针对它而不是使用它进行设计,所以我正在寻找见解)
在这种情况下,我会将流程分解为 return 承诺的几个函数,然后将它们链接在一起。
function getEnemies (obj) {
return new Promise(function (resolve) {
Enemy.find(... function(err,enemies) {
if (err) {throw err;}
obj.enemies = enemies.map(function (enemy) {
return {
playerID: enemy.combatTargetID,
enemy: enemy,
processed: false
};
});
resolve(obj);
});
});
}
function getPlayers (obj) {
return Promise.all(obj.enemies.map(function (enemy) {
return new Promise(function (resolve) {
Player.findOne({_id: enemy.combatTargtID}, function(err,player) {
if (err) {throw err;}
enemy.player = player;
resolve();
});
})
}).then(function () {
return obj;
});
}
function doWork() {
getEnemies()
.then(getPlayers)
.then(function (obj) {
console.log(obj); // do stuff with enemies and players here
}).catch(function (err) {
console.log(err, err.stack);
});
}