为什么这个 while 循环不会 运行?

Why will this while-loop not run?

我正在尝试学习 Javascript 中的 while 循环。为什么这个 while 循环 运行 即使单击按钮并且布尔值变为真?甚至可以让 while 循环对事件驱动的变量变化做出反应吗?

let run = false;
let count = 0;


const button = document.getElementById("startstop");
const counter = document.getElementById("counter");

button.addEventListener('click', function() {

  run = !run; //clever way to toggle the boolean

})

while (run == true) {
  count++;
  counter.innerHTML = count;
}
<button id="startstop">Start/Stop</button>

<p id="counter">0</p>

循环没有执行,因为运行为假。

这里我们可以将 while 移到按钮上,但这不会起作用,因为紧密循环没有给 DOM 时间来更新。

button.addEventListener('click', function() {
  run = !run; //clever way to toggle the boolean
  while (run) {
    count++;
    counter.innerHTML = count;
  }
})

我完全不建议使用 while 循环来更新 DOM

这会起作用,但即使不更新计数器,循环也会运行

let run = false;
let count = 0;


const button = document.getElementById("startstop");
const counter = document.getElementById("counter");

button.addEventListener('click', function() {

  run = !run; //clever way to toggle the boolean

})

setInterval(function() {
  if (!run) return
  count++;
  counter.innerHTML = count;
},10)
<button id="startstop">Start/Stop</button>

<p id="counter">0</p>

更优雅的版本是 clearInterval

let run = false;
let count = 0;
let tId;

const button = document.getElementById("startstop");
const counter = document.getElementById("counter");

button.addEventListener('click', function() {
  run = !run; //clever way to toggle the boolean
  if (run) tId = setInterval(function() {
    count++;
    counter.innerHTML = count;
  }, 10)
  else clearInterval(tId)
})
<button id="startstop">Start/Stop</button>

<p id="counter">0</p>

while 是 运行 但是当页面加载时,所以当您单击按钮时 while 循环已经通过。当您将 while 循环放在点击侦听器中时,您可以看到这一点,但这不是您想要的行为。而是使用间隔来解决此问题,如下所示:

let run = false;
let count = 0;
let interval = null;
const delay = 100;

const button = document.getElementById("startstop");
const counter = document.getElementById("counter");

button.addEventListener('click', function() {
  run = !run;
  
  if (run === true) {
    interval = setInterval(() => {
      count++;
      counter.innerHTML = count;
    }, delay)
    
  } else {
    clearInterval(interval)
  }
})
<button id="startstop">Start/Stop</button>
<p id="counter">0</p>

当您启动页面时,while 循环实际上是第一次结束。切换布尔值时,它不会自动为您重新呈现 dom。所以如果你想 运行 while 循环,你需要将它包装在一个函数中并调用它。

PS 运行 顺便说一下,此代码会冻结您的浏览器。尝试添加延迟

let run = false;
let count = 0;

button.addEventListener('click', function() {
  run = !run; //clever way to toggle the boolean
  doRun();
})

const doRun = () => {
   while (run) {
     count++;
     counter.innerHTML = count;
   }
}
doRun()
<button id="startstop">Start/Stop</button>

<p id="counter">0</p>

检查 while (run == true) { 在添加事件侦听器后立即发生,此时 run 的值仍然是 false

此外,永不停止的 while 循环将完全淹没线程,使用户无法单击按钮。

您可以使用 setInterval,它每 x 毫秒运行一个函数。 这样,您可以随时检查 run 的值,而不会阻塞线程(这是 while (run == true) { 所做的)。

let run = false;
let count = 0;
let intervalID = setInterval(tick, 1000); // 1000ms = 1sec

const button = document.getElementById("startstop");
const counter = document.getElementById("counter");

button.addEventListener('click', function() {
  run = !run;
  button.textContent = run ? "Stop" : "Start";
});

function tick() {
  if (run) {
    count++;
    counter.innerHTML = count;
  }
}
<button id="startstop">Start</button>

<p id="counter">0</p>

这样做的问题是,每次重新启动计时器时,您可能会关闭将近一整秒。要改进时间测量,您可以改为使用变量 count 来存储计数器打开时实际经过的毫秒数。使用另一个变量来存储最后一次测量发生的时间戳(或最后一次单击开始按钮的时间)也会大大提高准确性。

let run = false;
let count = 0;
let previousTick = Date.now();
let intervalID = setInterval(tick, 100); // 100ms = 0.1sec

const button = document.getElementById("startstop");
const counter = document.getElementById("counter");

button.addEventListener('click', function() {
  run = !run;
  button.textContent = run ? "Stop" : "Start";
  previousTick = Date.now();
});

function tick() {
  if (run) {
    count += Date.now() - previousTick;
    counter.innerHTML = Math.floor(count / 1000);
    previousTick = Date.now();
  }
}
<button id="startstop">Start</button>

<p id="counter">0</p>