Andy Bay
Andy Bay

Reputation: 15

Why will this while-loop not run?

I'm trying to learn while-loops in Javascript. Why won't this while loop run even when the button is clicked and the boolean becomes true? Is it even possible to have while-loops react to event-driven variable changes?

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>

Upvotes: 1

Views: 191

Answers (4)

Arcturus
Arcturus

Reputation: 27055

When you start up the page, the while loop actually ends the first time. When toggling the boolean, it will not automatically rerender the dom for you. So if you want to run the while loop, you'll need to wrap it in a function and call it.

PS Running this code will freeze your browser btw. Try adding in a delay

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>

Upvotes: 0

mplungjan
mplungjan

Reputation: 177691

The loop is not executed because run is false.

Here we can move the while to the button but that will not work because the tight loop does not give the DOM time to update.

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

I do NOT recommend to use while loops at all to update DOM

This will work but the loop will run even when not updating the counter

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>

A more elegant version would be to 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>

Upvotes: 1

nick zoum
nick zoum

Reputation: 7285

The check while (run == true) { happens right after the event listener is added, at which points the value of run is still false.

Moreover a while loop that never stops is going to completely overwhelm the thread, making it impossible for the user to click the button.

You could use setInterval, which runs a function every x milliseconds. That way, you can check the value of run whenever you want, without blocking the thread (which is what the while (run == true) { does).

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>

The problem with this is that you could be off by close to a whole second each time you restart the timer. To improve the time measuring you could instead use the variable count to store the actual milliseconds passed while the counter is on. Using another variable to store the timestamp at which the last measurement happened (or when the start button was last clicked) would also greatly improve the accuracy.

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>

Upvotes: 0

Markiesch
Markiesch

Reputation: 761

The while is running but when the page loads, so when you click on the button the while loop has already passed. You can see this in action when you place your while loop inside the click listener, however this is not the behaviour you want. Instead use an interval to fix this issue like shown below:

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>

Upvotes: 1

Related Questions