【问题标题】:In a While loop setTimeout not working在 While 循环中 setTimeout 不起作用
【发布时间】:2017-11-13 03:23:10
【问题描述】:

我 11 岁,刚开始编程。我试图使用一个while循环,我希望它以块的形式显示答案,而不是一大块。所以我尝试使用 setTimeout 但它会在一秒钟后显示第一行,然后立即将其余部分显示为一个大块。即使我不知道它是从哪里来的,它也会使用数字 9 作为临时变量。提前致谢。

我想在代码中保留 while 循环,因为我正在学习如何使用它们,所以如果您可以在答案中保留 while 语句,那就太棒了!

这是我的代码:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Made with Thimble</title>
    <link rel="stylesheet">
</head>

<body bgcolor="lightblue">
    <div id="d2">
        Divisible by 2
    </div>
    <div id="d3">
        Divisible by 3
    </div>
    <div>
        <input id="seed" type="number" placeholder="seed" value="3"><br>
        <input id="max" type="number" placeholder="max" value="8">
    </div>
    <button onclick="count()">Count</button>
    <div id="output"></div>
    <script>
    function count() {

        var seed = document.getElementById("seed").value
        var max = document.getElementById("max").value
        var temp = 1
        var output = document.getElementById("output")
        temp = seed
        console.log("seed:" + seed)
        console.log("max:" + max)
        document.getElementById("output").innerHTML = " "
        while (temp <= max) {

            var intdivby2 = temp % 2
            var intdivby3 = temp % 3

            setTimeout(function() {
                document.getElementById("output").innerHTML += "remainder of " + temp + " divided 
                by 2 is: "+intdivby2.toString()+" < br > "
                document.getElementById("output").innerHTML += "remainder of " + temp + " divided 
                by 3 is: "+intdivby3.toString()+" < br > "
                temp++
            }, 1000)

        }
    }
    </script>
</body>
</html>

【问题讨论】:

  • 我对你想要它做什么有点困惑。您是否希望它每秒显示一行,而不是在一秒钟后显示全部?
  • @Glen Pierce 这正是我想要的。谢谢
  • “刚开始编程”在一月份你说你是几个月前开始的,事实上你很年轻而且对编程相当陌生,这没有任何意义。只需提出一个简洁的问题,您就会得到答案并避免投反对票。
  • 嘘,让他松懈一点。这是一个非常合理的问题,他只是误解了 setTimeout 的工作原理。
  • 您之前是否编写过任何异步程序(setTimeout、ajax 等)?它不能很好地切割循环。不过,学习递归将是一个很好的动机。

标签: javascript while-loop settimeout


【解决方案1】:

我知道你想在那里做什么,但不幸的是它不会按照你的想法工作。

setTimeout() 不会暂停您的其余代码,它只是说“在运行我内部的内容之前等待这段时间”,同时脚本的其余部分将继续运行。因此,您的循环不是在循环的每个步骤之间延迟一秒钟,而是一个接一个地运行整个集合,然后一秒钟后,您的所有 setTimeouts 一个接一个地触发。

在 javascript 中做你想做的事情的更典型的方式(导致一段代码重复运行,每次重复之间有延迟)是setInterval()——它的工作方式几乎与setTimeout() 完全一样,只是它不断触发每个间隔到期的时间(直到您使用clearInterval() 取消它。)例如:

function count() {
  var seed = document.getElementById("seed").value
  var max = document.getElementById("max").value
  var temp = 1
  var output = document.getElementById("output")
  temp = seed
  // you already captured the "output" node, so we can reuse it here (and below).
  output.innerHTML = " "

  var theInterval = setInterval(function() {
    var intdivby2 = temp % 2
    var intdivby3 = temp % 3
    
    // no need to call .toString() on these vars; the fact that we're
    // adding them to another string will cause that to happen automatically
    output.innerHTML += "remainder of " + temp + " divided  by 2 is: "+intdivby2+" <br> "
    output.innerHTML += "remainder of " + temp + " divided  by 3 is: "+intdivby3+" <br> "
    temp++

    if (temp > max) {
      clearInterval(theInterval); // this stops the loop
    }
  }, 1000);
}
<div>
  <input id="seed" type="number" placeholder="seed" value="3"><br>
  <input id="max" type="number" placeholder="max" value="8">
</div>
<button onclick="count()">Count</button>
<div id="output"></div>

不过,作为一个练习,可以通过在单步执行循环时更改每个 setTimeout 的持续时间来组合 whilesetTimeout - 这样您就可以同时启动所有 setTimeouts(几乎),但会增加每个人在触发前等待的时间。

但有一个问题:您需要每个 setTimeout 为 temp 使用不同的值。如果您尝试在一个函数中处理这一切,那么在第一个 setTimeout 触发时,while 循环已经在循环中一直递增变量,因此每个 setTimeout 最终都会使用应该是最后一次迭代的值. (您的代码中也存在同样的问题:这就是您在第一次迭代中看到“9”的原因。)

如果您尝试通过等待增加变量直到进入 setTimeout 来解决这个问题,您只会以无限循环结束,因为第一次增加不会发生,直到第一个 setTimeout 触发。所以这也不好。 (当你使用while 时,经常会出现无限循环,这就是为什么我在几乎任何情况下都避免使用它的原因。)

您可以通过调用具有每次迭代所需的特定值的第二个函数来解决此问题,并让该辅助函数设置其自己的超时时间:

function count() {
  var seed = document.getElementById("seed").value
  var max = document.getElementById("max").value
  var temp = seed
  var i = 1 // or use 0 to have the first iteration fire immediately
  while (temp <= max) {
    handleIteration(temp, i);
    temp++
    i++
  }
}

function handleIteration(temp, i) {
  // temp and i are safely scoped within this function now, we don't 
  // need to worry about the while loop changing them
  setTimeout(function() {
    var intdivby2 = temp % 2
    var intdivby3 = temp % 3
    var output = document.getElementById('output')
    output.innerHTML += "remainder of " + temp + " divided  by 2 is: " + intdivby2 + " <br> "
    output.innerHTML += "remainder of " + temp + " divided  by 3 is: " + intdivby3 + " <br> "
  }, i * 1000) // <-- changes the duration of the timeout
}
<div>
  <input id="seed" type="number" placeholder="seed" value="3"><br>
  <input id="max" type="number" placeholder="max" value="8">
</div>
<button onclick="count()">Count</button>
<div id="output"></div>

【讨论】:

    【解决方案2】:

    将循环与超时混合是一个坏主意,而是使用间隔并在条件不再为真时将其删除

    var intervalId = setInterval(function () {
        var intdivby2 = temp % 2;
        var intdivby3 = temp % 3;
        document.getElementById("output").innerHTML += "remainder of " + temp + " divided by 2 is: " + intdivby2.toString() + " <br/> ";
        document.getElementById("output").innerHTML += "remainder of " + temp + " divided by 3 is: " + intdivby3.toString() + " <br/> ";
        if (++temp > max) clearInterval(intervalId);
    }, 1000);
    

    小例子

    var index = 0;
    var intervalId = setInterval(function() {
      var text = "Iteration: " + index + "<br/>";
      document.getElementById("output").innerHTML += text;
      if (++index > 10) clearInterval(intervalId);
    }, 1000);
    #output {
      max-height: calc(100vh - 130px);
      overflow: auto;
    }
    &lt;div id="output"&gt;&lt;/div&gt;

    另一种方法是使用递归超时

    (function asyncLoop() {
        setTimeout(function () {
            var intdivby2 = temp % 2;
            var intdivby3 = temp % 3;
            document.getElementById("output").innerHTML += "remainder of " + temp + " divided by 2 is: " + intdivby2.toString() + " <br/>";
            document.getElementById("output").innerHTML += "remainder of " + temp + " divided by 3 is: " + intdivby3.toString() + " <br/>";
            if (++temp <= max) asyncLoop();
        }, 1000);
    })();
    

    小例子

    var index = 0;
    (function asyncLoop() {
      setTimeout(function() {
        var text = "Iteration: " + index + "<br/>";
        document.getElementById("output").innerHTML += text;
        if (++index <= 10) asyncLoop();
      }, 1000);
    })();
    #output {
      max-height: calc(100vh - 130px);
      overflow: auto;
    }
    &lt;div id="output"&gt;&lt;/div&gt;

    【讨论】:

      【解决方案3】:

      异步while循环(由于一些很棒的ES7东西(只有最新的浏览器支持它)):

      function timer(t){
        return new Promise(r=>setTimeout(r,t));
      }
      
      async function main(){
        var a=0;
        while(a<10){
          alert(a);
          a+=1;
          await timer(1000);//wait one second
        }
      }
      main();
      

      您的代码不起作用,因为 setTimeout 不会阻止当前 funcioj 执行。它将在后台启动一个计时器,然后将传递的函数推送到所谓的 qeue 上,该函数在主线程停止时执行。

      var a=true;
      while(a){
       setTimeout(_=> a=false,1000);
       //will run forever as a is never set to false
      }
      //the main code would exit here and the timers are somewhen executed
      //unreachable point were a=false...
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-10-11
        • 2015-11-30
        • 2016-04-19
        • 2011-10-19
        • 1970-01-01
        • 2012-07-18
        • 2015-08-02
        相关资源
        最近更新 更多