我试图使一个函数返回id是否为validId。
但是,事实证明,有时即使await isValidId(230)truevalidId也不是230。

我认为这是因为承诺总是异步解决的。那正确吗?那我应该如何设计这个功能呢?

let validId = 230;
let isValidId = function(id){
  return new Promise(resolve => {
    //async code
    resolve(validId === id);
  });
}

if (await isValidId(230)){
  //validId is not necessary 230
}


这是一个更完整的示例。

let playerAskedToLogOut = false;
let playersOnline = ['MyUsername'];
let canPlayerUseItem = async function(player,itemId){
  let hasItem = await Database.playerHasItem(player, itemId);
  let isStillOnline = playersOnline.includes(player);
  playerAskedToLogOut = true;  //normally called by another function
  return hasItem && isStillOnline;
};

setInterval(() => {
  useItems();
  logOutPlayers();
}, 100);

let useItems = async function(){
  if (await canPlayerUseItem('MyUsername', 'hammer')){
    //the player is not online anymore. Yet, await canPlayerUseItem returned true
  }
}
let logOutPlayers = function(){
  if(playerAskedToLogOut)
    playersOnline = []
}

最佳答案

问题中的代码存在语法错误:它在非await函数中使用了async,这是行不通的。 (您已再次编辑代码以制作canPlayerUseItem async

第一:在new Promise函数中使用async根本没有任何目的。该功能自动为您创建一个承诺。因此该代码(在撰写本文时,您一直在对其进行更改)实际上应该是:

let canPlayerUseItem = async function(player,itemId){
  let hasItem = await Database.playerHasItem(player, itemId);
  let isStillOnline = playersOnline.includes(player);
  return hasItem && isStillOnline;
};


canPlayerUseItem的诺言将用true解决的唯一原因是,当我们进行检查时,如果hasItem返回真实且playersOnline包含player。 > ..如果播放器在呼叫playerHasItem时不联机,但在canPlayerUserItem检查完成时处于联机状态,则它将以playerHasItem解析。

如果要在等待true调用之前进行playersOnline检查:

let canPlayerUseItem = async function(player,itemId){
  if (playersOnline.includes(player)) {
    return false;
  }
  return await Database.playerHasItem(player, itemId);
};


或者,如果您想同时检查两者(但它们可能已脱机,则可以在两者之间重新联机):

let canPlayerUseItem = async function(player,itemId){
  if (playersOnline.includes(player)) {
    return false;
  }
  let hasItem = await Database.playerHasItem(player, itemId);
  let isStillOnline = playersOnline.includes(player);
  return hasItem && isStillOnline;
};




在您的最新编辑中,您建议在解决承诺后将某些东西从在线阵列中删除。这向我暗示您正在询问如何使用playerHasItem在代码中知道播放器仍处于在线状态。

答:不能。您在canPlayerUseItem中所做的任何事情都不能保护您免受此内在的竞争状况的影响:

if (await canPlayerUseItem(player, itemId)) {
    // They player may now be offline
}


...因为玩家有可能在canPlayerUseItem完成检查之后但在消耗该结果的代码运行之前退出。这就是异步代码的本质。上面基本上是这样的:


canPlayerUseItem
在此事件循环迭代结束之前的收益
flag = canPlayerUseItem(player, itemId)(此时播放器可能不再登录)


如果需要进行检查,则必须在异步函数之外自己进行检查:

if (await Database.playerHasItem(player, itemId) && playersOnline.includes(player)) {
    // As of this event loop, the player is online, and had the item
    // when we checked a moment ago
}


这是:


if (flag) ...
在此事件循环迭代结束之前的收益
flag = Database.playerHasItem(player, itemId) ...

09-25 17:18