【问题标题】:How can I execute a statement AFTER a loop finishes in javascript?在javascript中循环完成后如何执行语句?
【发布时间】:2017-06-30 21:09:58
【问题描述】:

我正在查询一个 mongo 数据库以检索用于在 rougelike 游戏中显示的图块。这是我使用的功能:

function get_display(){
    var collections = ['austinsroom'];
    var db  = mongojs(uri, collections);
    var temphtml = '';

    for(var j = 0; j < 3; j++) {
        console.log("y=" + String(j));
        db.austinsroom.find({"y": j}, {}).sort({"x": 1}, function(err, records) {
            if(err) {
                console.log("There was an error executing the database query.");
                return;
            } 
            var i = records.length;
            while(i--) {
                temphtml += records[i].display;
            }   
            temphtml += '<br>';
            //console.log(temphtml);
            //return temphtml;
            //THE ONLY WAY I CAN GET ANYTHING TO PRINT IN THE CONSOLE IS IF I PUT IT INSIDE THE LOOP HERE
            });
        //console.log(temphtml);
        //return temphtml;
        //THIS DOES NOTHING
    }
    //console.log(temphtml);
    //return temphtml;
    //THIS DOES NOTHING
}
get_display();

如果我将console.log(temphtml) 放入循环中,它会打印三遍,这不是我想要的。我只想要最终的字符串(即...&lt;br&gt;...&lt;br&gt;...&lt;br&gt;。而且我最终无法返回temphtml 字符串,这实际上是重要的。这是javascript的一些怪癖吗?为什么它不会在循环后执行语句?

另外:有没有更好的方法来检索存储在 mongo 数据库中的网格的每个元素,以便它可以正确显示?以下是 mongo 文档的样子:

{
    "_id": {"$oid": "570a8ab0e4b050965a586957"},
    "x": 0,
    "y": 0,
    "display": "."
}

现在,游戏应该使用 x 和 y 坐标值在所有空白处显示“.”。数据库由“x”值索引。

【问题讨论】:

  • 我不确定db.austinsroom.find 是什么,但它是一个异步函数吗?如果是,则意味着 for 循环可能在 db.austinsroom.find 调用之前完成,在这种情况下,temphtml 变量中将没有任何内容...
  • @IMTheNachoMan 关于db.austinsroom.find 是异步的是正确的(注意回调函数)。听起来你想要的是循环通过db.austinsroom.find 1,2,3 THEN 打印。由于 1、2、3 包含异步调用,因此您需要找到一种方法来知道它们何时全部完成(或失败)。研究使用承诺
  • @KarlGalvez 但实际上有一些方法可以使用异步流控制“循环”,或者构建要返回的承诺列表。注意 mongojs 驱动程序本身不支持承诺,但 MongoDB 的其他驱动程序支持。

标签: javascript node.js mongodb mongodb-query


【解决方案1】:

async.whilst。您希望对 for 循环进行流控制,这提供了一个回调来控制每个循环迭代。

var temphtml = "",
    j = 0;

async.whilst(
  function() { return j < 3 },
  function(callback) {
    db.austinsroom.find({"y": j }, {}).sort({"x": 1}, function(err, records) 
      temphtml += records.map(function(el) {
          return el.display;
      }).join("") + '<br>';
      j++;
      callback(err);
    });
  },
  function(err) {
     if (err) throw err;
     console.log(temphtml);
  }
)

或者使用Promise.all()收集的承诺返回“一个大的结果”。但是您还需要从mongojs 切换到promised-mongo,作为最接近的等价物,因为实际上有更多的mongodb 驱动程序支持promise。那个只是来自mongojs的直接分叉:

var temphtml = "",
    j = 0,
    promises = [];

for ( var j=0; j < 3; j++ ) {
   promises.push(db.austinsroom.find({"y": j }, {}).sort({"x": 1}).toArray());
   promises.push('<br>');   // this will just join in the output
)

Promise.all(promises).then(function(records) {
    temphtml += records.map(function(el) {
        return el.display;
    }).join("");
})

不完全相同,因为它是一个列表输出而不是三个,但重点是 Promise 对象延迟到实际调用来解析,因此您可以在循环中提供参数,但稍后执行。

【讨论】:

  • 在我 npm 安装 async 后,您的第一个解决方案效果很好。我仍然不太明白为什么或承诺,但这完全取决于我:/
  • @AustinCapobianco 答案向您显示的是async.whilst 在迭代器上有一个callback,因此仅当从结果@987654337 内部调用callback 时才调用下一个循环@ 打回来。承诺,意味着.find() 语句实际上并没有执行,而是实际上返回了一个“承诺”。然后这些都用.then()“解决”(执行)。但是对于Promise.all(),它们会同时发生,并将输出“组合”为单个数组。除了依赖关系之外,不同之处在于执行顺序。分别是连续的或“一次全部”。
  • 在迭代后我仍然不明白callback(err); 的用途。此外,虽然console.log(temphtml) 有效,但我不知道如何使函数返回temphtml 字符串。它只是说当我尝试console.log(get_display()); 时结果未定义
  • @AustinCapobianco 因为你做错了。阅读"How do I return the response from an asynchronous call?",它基本上包含有关该主题的明确答案。至于callback(err),请继续查看并进一步阅读并解决问题。一旦你理解了回调,它就会变得非常明显。
【解决方案2】:

我不使用 MongoDB,但我正在阅读它是异步的。所以发生的事情是您的 db.austinsroom.find 调用触发另一个“线程”并返回到 for 循环以继续下一次迭代。

做你想做的事情的一种方法是在你的db.austinsroom.find函数的末尾检查你是否完成了for循环。比如:

function get_display()
{
    var collections = ['austinsroom'];
    var db  = mongojs(uri, collections);
    var temphtml = '';
    var doneCounter = 0;

    for(var j = 0; j < 3; j++)
    {
        console.log("y = " + String(j));
        db.austinsroom.find({"y": j}, {}).sort({"x": 1}, function(err, records)
        {
            if(err)
            {
                console.log("There was an error executing the database query.");
                return;
            } 
            var i = records.length;
            while(i--)
            {
                temphtml += records[i].display;
            }   
            temphtml += '<br>';

            // we're done with this find so add to the done counter
            doneCounter++;

            // at the end, check if the for loop is done
            if(doneCounter == 3)
            {
                // done with all three DB calls
                console.log(temphtml);
            }
       });
    }
}

这可能不是最好的方法,因为我对 MongoDB 一无所知,但它应该让你走上正确的道路。

【讨论】:

  • 试过了,但没用。这对我来说没有任何意义。我什至在 if 语句中尝试了“j=1”和“j=2”,但没有任何效果......
  • Promises 仍然是 IMO 更好的答案,但需要让您的 mongodb 调用返回它们。但是,我认为如果您使用function(err, records, j),此解决方案将起作用,目前此功能不知道j(我认为)。如果没有,请尝试在if(j == 3) 语句之前记录 j 的值
  • 当我在 if 语句中调用 console.log(temphtml) 时,它只会给我初始化它时的空白 temphtml='',不管 j 是什么。 j 值很好。我现在正试图弄清楚 Promise,但我并不真正理解它们。
  • 我现在看到了这个问题。 db.austinsroom.find 的顺序不保证是 1,2,3 的顺序。
  • 是的,Promise 会更好,但不确定用 MongoDB 实现会有多痛苦。
猜你喜欢
  • 2012-12-04
  • 2020-10-27
  • 2014-12-14
  • 2020-11-21
  • 1970-01-01
  • 1970-01-01
  • 2023-04-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多