在 While 循环中,setTimeout 不起作用。

5
我是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
@Glen Pierce 这正是我想要的,谢谢。 - HyperMonkey
你在一月份说你刚刚开始编程,但你已经开始了几个月了,你年轻且对编程相对陌生并不代表什么。只要简明扼要地提出问题,你就会得到答案并避免贴负分。 - Nick stands with Ukraine
1
别这么苛刻嘛,这是一个非常合理的问题,他只是误解了setTimeout的工作原理。 - Daniel Beck
1
你之前有写过任何异步的程序(例如setTimeout,ajax等)吗?它与循环不太搭配。不过这会是学习递归的好动力。 - Bergi
显示剩余2条评论
3个回答

5
我看到你想做的事情,但很遗憾,它不会按照你的想法工作。 setTimeout() 不会暂停你的代码,它只是说“在我里面等待这段时间后运行里面的东西”,与此同时,脚本的其余部分将继续运行。因此,你的循环中每一步之间不是有一秒的延迟,而是整个循环一次性全部运行完,然后一秒钟后,所有的 setTimeout() 一起触发。
在 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 的持续时间来结合 while 和 setTimeout。这样做的好处是几乎同时启动了所有 setTimeout,但在每个 setTimeout 触发前增加了等待时间。
但是有一个问题:需要为 temp 使用不同的值。如果尝试在一个函数中处理所有内容,则第一个 setTimeout 触发时,while 循环已经通过整个循环递增了变量,因此每个 setTimeout 最终都会使用来自最后一次迭代的值(您在代码中也遇到了相同的问题,这就是为什么在第一次迭代中看到“9”的原因)。
如果尝试等到在 setTimeout 中递增变量,那么将得到一个无限循环,因为第一个递增直到第一个 setTimeout 触发才会发生。所以这也不太好。(当使用 while 时,无限循环通常会发生,这就是为什么我在几乎任何情况下都避免使用它的原因。)
您可以通过调用第二个函数并具有每个迭代所需的特定值来解决这个问题,并使该辅助函数设置自己的 timeout 和持续时间:

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;
}
<div id="output"></div>

另一种方法是使用递归超时。
(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;
}
<div id="output"></div>


2
异步 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无法停止当前函数的执行。它会在后台启动一个计时器,然后将传递的函数推入所谓的队列中,在主线程停止时执行。

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...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接