0

我对 JavaScript 很陌生,遇到了一个我找不到解决方案的问题。我正在为我的 APCS 最终项目开发一款游戏,并尝试为其添加排行榜。为了找到顶级玩家,我将所有高分放入一个数组中,从高到低排序,然后搜索分数与数组中前 5 个数字匹配的 5 个用户名。我正在使用 AppLab 来创建它,并且 AppLab 具有内置的数据库功能,这就是“readRecords 的用途。但我的问题是,当我使用 for 循环填充数组时,人口不存在于该函数即使数组变量是在函数之外创建的,这里的代码...

function leaderGrabEasy() {
  var leaderScores = [];
  var leaders = [];
  readRecords("userData",{},function(records) {
    for (var i = 0; i < records.length; i++) {
      leaderScores.push(records[i].E_highscore);
    }
    leaderScores.sort(function(a, b){return b-a});
  });
  readRecords("userData",{E_highscore:leaderScores[0]},function(records) {
    for (var i = 0; i < records.length; i++) {
      leaders.push(records[i].username);
    }
    console.log(leaders);
  });
}

当我尝试为“leaderScores [0]”中的任何内容读取数据库列“E_highscores”时,就会出现此问题

readRecords("userData",{E_highscore:leaderScores[0]},function(records) {

但是因为数组在第一个函数之外是空的,所以数组中的那个位置是空的。提前感谢您的帮助!

-在室内

4

2 回答 2

0

readRecords是一个异步函数,对吗?

问题是你有一个竞争条件。这意味着代码需要以非常特定的顺序执行才能工作,但您目前无法控制该顺序。

分数被推送到leaderScore传递给的回调内部readRecords。但是,在下一行(调用之后readRecords),您尝试readRecords再次调用,其值填充第一个的回调中readRecords

基本上,您的代码按以下顺序执行:

readRecords("userData",{},callback1)
readRecords("userData",{E_highscore:leaderScores[0]},callback2)
callback1()
callback2()

事实上,不能保证callback1之前会发生callback2

有许多工具可以解决这个异步排序问题,比如 Promises 等。我将使用最简单的选项,然后重新排列您的代码。这将创建嵌套回调,但目前,它可以工作并且(希望)帮助您了解这一原则的实际应用。

function leaderGrabEasy() {
  var leaderScores = [];
  var leaders = [];
  readRecords("userData",{},function(records) {
    for (var i = 0; i < records.length; i++) {
      leaderScores.push(records[i].E_highscore);
    }
    leaderScores.sort(function(a, b){return b-a});

    readRecords("userData",{E_highscore:leaderScores[0]},function(records) {
     for (var i = 0; i < records.length; i++) {
        leaders.push(records[i].username);
     }
    console.log(leaders);
     });
  });
}

现在第二个readRecords只会在您填充数组之后发生,而不是之前的任何时间。


这确实引发了其他可重用性问题。如果你想稍后做更复杂的事情,或者添加更多回调,甚至是循环怎么办?好吧,这将取决于你哈哈。但是,如果您牢记我们在这里讨论的原则,您肯定可以成功地做到这一点:任何异步回调都可以随时调用因此请始终牢记这一点。

问自己一个问题:如果在 1 毫秒、500 毫秒和 1 分钟内调用此回调(同步地,按照编写顺序),我的代码会工作吗?如果是这样,那么它在逻辑上是合理的。

于 2018-04-22T23:45:24.063 回答
0

将您的函数转换为承诺返回函数而不是嵌套回调可能会更好。代码现在可能很容易维护,但是当你写得越来越多时,解开后记就会变得越来越难。

//readRecords as a promise returning function
function readAsPromise(str,obj){
  return new Promise(
    function (resolve,reject){
      readRecords(
        str,
        obj,
        function(records,error) {//this readRecords cannot fail?
          if(error){
            reject(error);return;
          }
          resolve(records);
        }
      );
    }
  );
}

function leaderGrabEasy() {
  return Promise.all([
    readRecords("userData",{}),
    readRecords("userData",{E_highscore:leaderScores[0]})
  ])
  .then(
    function(results){
      return {
        leaderScores:results[0]
          .map(function(leader){return leader.E_highscore;})
          .sort(function(a, b){return b-a}),
        leaders:results[1]
          .map(function(leader){return leader.username})
      }
    }
  )
}

//use the function:
leaderGrabEasy()
.then(
  function(result){
    console.log("result:",result);
  }
)
.catch(
  function(error){
    console.log("there was an error:",error);
  }
)

//arrow function syntax:
leaderGrabEasy()
.then(result=>console.log("result:",result))
.catch(error=>console.log("there was an error:",error));

在现代语法中,它看起来像这样:

//readRecords as a promise returning function
const readAsPromise = (str,obj) =>
  new Promise(
    function (resolve,reject){
      readRecords(
        str,
        obj,
        function(records,error) {//this readRecords cannot fail?
          if(error){
            reject(error);return;
          }
          resolve(records);
        }
      );
    }
  );

const leaderGrabEasy = () =>
  Promise.all([
    readRecords("userData",{}),
    readRecords("userData",{E_highscore:leaderScores[0]})
  ])
  .then(
    ([score,name])=>({
      leaderScores:score.map(leader=>leader.E_highscore).sort((a, b)=>b-a),
      leaders:name.map(name=>nae.username)
    })
  )

//use the function:
leaderGrabEasy()
.then(result=>console.log("result:",result))
.catch(error=>console.log("there was an error:",error));
于 2018-04-23T04:08:51.360 回答