Your code is wrong. If the timer value is non-zero, you loop forever, constantly queuing events to decrement the same initial counter value by 1 (e.g. changing 7 to 6 over and over, but never 6 to 5), but because the dispatch code never finishes, control is never handed back to the JS event loop to allow it to process any of those events.
setTimeout
isn't just "sleep the specified time and then run this function", and it doesn't block; it just schedules the call to occur later, and returns instantly. The scheduled call is only processed when the event loop has control to dispatched events; since your code loops forever, the event loop never regains control.
You need to think asynchronously in JS; as soon as you invoke an async feature, you can't rely on the results anywhere but in the callback. Try restructuring your code to perform the subsequent work in asynchronously, so you actually see the updated values:
function decrementTimerUntilZero() {
var x = getTimerValue();
if (x >= 1) {
setTimerValue(x - 1);
setTimeout(decrementTimerUntilZero, 1000); // Schedule another decrement
} else {
setTimerValue(0);
}
}
if (getTimerValue() > 0) {
setTimeout(decrementTimerUntilZero, 1000);
}
You initially schedule it for decrementing if it's positive, then each time you decrement, you schedule a new decrement if the value hasn't already reached zero.
An alternative approach (that would likely get more consistent timing, avoiding the problem of an event loop dispatch delay never being made up) would be to use setInterval
:
var timerinterval = setInterval(function() {
var x = getTimerValue();
if (x >= 1) {
setTimerValue(x - 1);
} else {
setTimerValue(0);
clearInterval(timerinterval);
}
}, 1000);
In both cases, you essentially give up control when your function is finished to let the event loop do more work, trusting that your callback will be invoked correctly in the future where it can resume work. JavaScript has no concept of a sleep
function (by design; sleeping would block the event loop until it finished, freezing the browser UI); this is the only way to properly do work intermittently.
If you want more information, I suggest you check out What do I do if I want a JavaScript version of sleep()? if you want to get a better idea of how setTimeout
works, why it isn't just a blocking sleep, etc.