Stop timer at zero - javascript

I am trying to get my countdown to stop at zero however it resets rather than stops.
I've added a conditional statement at the end of the runTimer function but nothing happens. It just resets.
I'm going off of an exercise where it counts up. I'm modifying it a bit and having it countdown.
function runTimer() {
let currentTime = leadingZero(timer[1]) + ":" + leadingZero(timer[2]);
theTimer.innerHTML = currentTime;
timer[3]--;
timer[0] = Math.floor((timer[3]/100)/60); //minutes
timer[1] = Math.floor((timer[3]/100) - (timer[0] * 60)); //seconds
timer[2] = Math.floor(timer[3] - (timer[1] * 100) - (timer[0] * 6000)); //hundredths
if (currentTime = 0) {
clearInterval(interval);
}
}
I expected it to stop at zero but it just resets back to 59:00... and I want it to stop at 00:00.

The problem is this part:
if (currentTime = 0)
Since you're checking if the value is 0, you don't want to assign a value of 0, instead you want to compare currentTime with 0. This is done with the === operator. So to summarize:
= is to assign a value to a variable. ( left is variable and right is the assignment)
== or === is to compare the two values.(Difference between == and === in JavaScript)
Your line should be:
if (currentTime == 0)
Hope it helped. :)

Two points.
1) As already mentioned, your if clause will not work because you are using "=" (a single equal sign). A single equal sign in JavaScript does assign values, not compare values. You however want to compare values and need to use double or triple equals.
2) Even if you change that, currentTime will probably never evaluate to zero, since you have assigned a string to currentTime before. So even if currentTime is "00:00", the string will not evaluate to 0 (see image)
I guess you more want to do something like this:
if (timer[2] === 0 && timer [1] === 0 && timer[0] === 0) {
clearInterval(interval);
}
Or most probably this will suffice:
if (timer[3] <= 0) {
clearInterval(interval);
}

Related

Number counts up before using an if statement to pause?

I want the number to stop when the variable num reaches a whole number. The pausing works correctly, but it goes up by 0.01 then pauses. The statement is ran every frame (via requestAnimationFrame).
if (Math.floor(num * 100) / 100 == Math.floor(num) && pause < 50) {
pause += 1;
} else {
pause = 0;
num += 0.01;
}
The complete code block is on GitHub:
https://github.com/BootLegAidan/Geometrical-Thing/blob/master/Update.js
The issue appears to be your Math.floor(num * 100 ) / 100 == Math.floor(num) condition evaluating to true even if num has a very small decimal value -- such as 1.000000007.
You can avoid that issue by comparing num to its rounded value directly:
if (num == Math.floor(num) && pause < 50) {
Compared that way, unless num is a whole number, the condition won't be true.

countdown from n to 0 in given time, negative end value

I am working on simple script that should animate given value (for example 6345.23) to 0 by counting it down, it should also end up at 0 if specified amount of time have passed (for example 2 seconds.
I started by simple logic:
given config: initial value, time in sec, interval
time is given in seconds so convert it to milliseconds
calculate amount of ticks by dividing time in ms by interval
calculate amount of decreased value per tick by dividing initial value by amount of ticks
once above are known we can simply do: (simple model, not actual code)
intId = setInterval(function() {
if(ticks_made === amount_of_ticks) {
clearInterval(intId);
} else {
value -= amount_per_tick;
// update view
}
}, interval);
actual code:
var value = 212.45,
time = 2, // in seconds
interval = 20; // in milliseconds
var time_to_ms = time * 1000,
amount_of_ticks = time_to_ms / interval,
amount_per_tick = (value / amount_of_ticks).toFixed(5);
var start_time = new Date();
var ticks_made = 0;
var intId = setInterval(function() {
if(ticks_made === amount_of_ticks) {
console.log('start time', start_time);
console.log('end time', new Date());
console.log('total ticks: ', amount_of_ticks, 'decresed by tick: ', amount_per_tick);
clearInterval(intId);
} else {
value = (value - amount_per_tick).toFixed(5);
console.log('running', ticks_made, value);
}
ticks_made++;
}, interval);
Link do fiddle (in console you can observe how it works)
If you set time to 2 (2 seconds) its ok, but if you set time to for example 2.55 (2.55 seconds) it doesnt stop at all at 0, its passing by and going indefinitely in negative values.
How i can fix it so no matter what is set in seconds its always go precisly one by one until reaches perfectly 0?
var value = 212.45,
time = 2, // in seconds
interval = 20; // in milliseconds
var time_to_ms = time * 1000,
amount_of_ticks = time_to_ms / interval,
amount_per_tick = (value / amount_of_ticks).toFixed(5);
var start_time = new Date();
var ticks_made = 0;
var intId = setInterval(function() {
if(ticks_made === amount_of_ticks) {
console.log('start time', start_time);
console.log('end time', new Date());
console.log('total ticks: ', amount_of_ticks, 'decresed by tick: ', amount_per_tick);
clearInterval(intId);
} else {
value = (value - amount_per_tick).toFixed(5);
console.log('running', ticks_made, value);
}
ticks_made++;
}, interval);
You're relying on ticks_made === amount_of_ticks being an exact match. Chances are, due to rounding, you won't get an exact match, so you'd be better off doing:
if(ticks_made >= amount_of_ticks) {
kshetline's answer correctly addresses why you get into negative values. When dealing with fractional IEEE-754 double-precision binary numbers (in the normal range, or even whole numbers in very high ranges), == and === can be problematic (for instance, 0.1 + 0.2 == 0.3 is false). Dealing with values as small as the fractional values here are, accumulated imprecision is also a factor. It's inevitable to have to fudge the final step.
But there's a larger issue: You can't rely on timers firing on a precise schedule. Many, many things can prevent their doing so — other UI rendering work, other scripts, CPU load, the tab being inactive, etc.
Instead, the fundamental technique for animation on browsers is:
Update when you can
Update based on where you should be in the animation based on time, not based on how many times you've animated
Use requestAnimationFrame so your update synchronizes with the browser's refresh
Here's your code updated to do that, see comments:
// Tell in-snippet console to keep all lines (rather than limiting to 50)
console.config({maxEntries: Infinity});
var value = 212.45,
time = 2.55, // in seconds
time_in_ms = time * 1000,
amount_per_ms = value / time_in_ms,
interval = 100 / 6, // in milliseconds, ~16.66ms is a better fit for browser's natural refresh than 20ms
ticks_made = 0;
// A precise way to get relative milliseconds timings
var now = typeof performance !== "undefined" && performance.now
? performance.now.bind(performance)
: Date.now.bind(Date);
// Remember when we started
var started = now();
// Because of the delay between the interval timer and requestAnimationFrame,
// we need to flag when we're done
var done = false;
// Use the interval to request rendering on the next frame
var intId = setInterval(function() {
requestAnimationFrame(render);
}, interval);
// About half-way in, an artificial 200ms delay outside your control interrupts things
setTimeout(function() {
console.log("************DELAY************");
var stop = now() + 200;
while (now() < stop) {
// Busy-loop, preventing anything else from happening
}
}, time_in_ms / 2);
// Our "render" function (okay, so we just call console.log in this example, but
// in your real code you'd be doing a DOM update)
function render() {
if (done) {
return;
}
++ticks_made;
var elapsed = now() - started;
if (elapsed >= time_in_ms) {
console.log(ticks_made, "done");
done = true;
clearInterval(intId);
} else {
var current_value = value - (amount_per_ms * elapsed);
console.log(ticks_made, current_value);
}
}
/* Maximize in-snippet console */
.as-console-wrapper {
max-height: 100% !important;
}
If you run that, then scroll up to the "************DELAY************" line, you'll see that even though rendering was held up by "another process", we continue with the appropriate next value to render.
It would make sense to convert the result of .toFixed() to a number right away:
let amount_per_tick = +(value / amount_of_ticks).toFixed(5);
let value = +(value - amount_per_tick).toFixed(5);
(note the + signs)
Then you will never have to worry about type coercion or anything, and instead just focus on math.

JS - Prevent long loop javascript from "crash" browser implementation

I have a long loop that takes maybe 10 mins or more, and I want to set always a new time to avoid it to continue. But it dosen't works.
function problem3(){
var img = document.getElementById('p_3');
img.style.display = img.style.display === 'block' ? 'none' : 'block';
var number=600851475143;
var t = new Date();
for(var i=3;i*i<=number;i+=2){
if(isPrime(i) && number%i==0){
var maxPrime = i;
}
setInterval(function(){time(t)},5000);
}
document.getElementById("p3").innerHTML = 'Il più grande divisiore primo di <span>'+number+"</span> è <span>" + maxPrime+"</span>";
}
function time(t){
return console.log(Date() - t);
}
If I put console.log(Date() - t);in the problem3() function it works, but I can't do Date()-t every 5 seconds, something like setInterval(Date()-t,5000)
This is a case where you might consider using the workers API. Instead of freezing the browser, let the job be done in the background and call back to the main thread when it's done.
https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API
JavaScript is not multithreaded. So we think of setInterval() as running a piece of code every n ms (5000 in your example). But that's not quite true. If there's already script running when the interval elapses, the best that can happen is the bit of code gets added to a queue to be executed - but nothing from that queue is going to run until the already-running script finishes.
So in rough terms that's why it's not working, but what to do? Well, if you want anything to happen before problem3() returns, then problem3() is going to have to make it happen in a synchronous way.
For example, you could create a lastOutputTime variable, initialize it to the current time, and on each iteration through the for loop compare the current time to the stored value. If 5 seconds have passed, output to console and update lastOutputTime.
Your algorithm should be improved to something like this:
function maxPrimeFactor(number) {
if (number == 0 || !Number.isInteger(number) ||
number > Number.MAX_SAFE_INTEGER) return NaN;
number = Math.abs(number);
while(number % 2 == 0) number /= 2;
for (var i = 3; i * i <= number; i += 2) {
while(number % i == 0) number /= i;
}
return number;
}
var number = 600851475143;
console.log('maxPrimeFactor(' + number + ') == ' + maxPrimeFactor(number));
If for some numbers you need too much time, then break the loop into smaller chunks and asynchronize. But never use setInterval for this, and especially never use setInterval inside a long loop. setInterval schedules some task to run every n milliseconds, so if you use it in a loop, after i iterations, the task will run i every n milliseconds! And setInterval is so problematic because it can freeze the browser if the task takes more than n milliseconds. You should use setTimeout instead.
However, this would be useless in this case. The algorithm above can detect that 304250263527209 (15 digits) is a prime almost instantly. Given that the maximum safe integer is 9007199254740991 (16 digits), I don't think you will have problems for any number.
If you say the algorithm takes so long, it may be because you are trying it with bigger numbers. But be aware JS numbers are 64-bit floating point numbers, and thus integers can't be represented accurately above Number.MAX_SAFE_INTEGER. You will get a wrong result anyways, so do not even try to calculate that.
In the case of the Project Euler #551, a brute-force approach would be
function sumOfDigits(n) {
var sum = 0;
while(n != 0) {
sum += n % 10;
n = Math.floor(n/10);
}
return sum;
}
function sumDigitsSeq(n) {
return new Promise(function(resolve) {
var i = 1;
var chunkSize = 1e5;
var sum = 1;
(function chunk() {
chunkSize = Math.min(chunkSize, n-i);
for (var j=0; j<chunkSize; ++j, ++i) {
sum += sumOfDigits(sum);
}
if (i >= n) return resolve(sum);
console.log('Please wait. sumDigitsSeq(' + i + ') == ' + sum);
setTimeout(chunk, 60);
})();
});
}
var number = 1e6;
sumDigitsSeq(number).then(function(result) {
console.log('Done! sumDigitsSeq(' + number + ') == ' + result);
});
Of course brute-force is not the appropriate way to solve the problem.

How to grow number smoothly from 0 to 5,000,000

This is probably basic math that I don't seem to remember.
I'm trying to get from 0 to 5,000,000 in 10 seconds while having all the numbers ticking. I don't have to have the number reach exactly 5,000,000 because I can just do a conditional for it to stop when it's over.
Right now I have this:
count+= 123456
if (count > 5000000) {
count = 5000000;
}
It gives the sense of number moving you know? But It really starts off too high. I wanted to gradually climb up.
You could do something like this:
function timedCounter(finalValue, seconds, callback){
var startTime = (new Date).getTime();
var milliseconds = seconds*1000;
(function update(){
var currentTime = (new Date).getTime();
var value = finalValue*(currentTime - startTime)/milliseconds;
if(value >= finalValue)
value = finalValue;
else
setTimeout(update, 0);
callback && callback(value);
})();
}
timedCounter(5000000, 10, function(value){
// Do something with value
});
Demo
Note that with a number as big as 5000000 you won't see the last couple digits change. You would only see that with a small number like 5000. You could fix that; perhaps by adding in some randomness:
value += Math.floor(Math.random()*(finalValue/10000 + 1));
Demo with randomness
You can tween:
import fl.transitions.Tween;
import fl.transitions.easing.Regular;
var count = 0;
var tween:Tween = new Tween(this, "count", Regular.easeInOut,0,5000000,10, true);
This will tween you variable count from 0 to 5000000 in 10 seconds. Read about these classes if you want to expand on this code.
Tween
TweenEvent
Good luck!

setInterval doesn't get fired

Below is the functions I use to run a function periodically. I use the function to change the background of the body. but it doesn't get fired for some reason. Please help me with this code.
setInterval(uiImageChanger(),1);
function uiImageChanger(){
var currentTime = new Date().getHours();
var images = ['image1.jpg','image2.jpg'];
if( currentTime > 00 && currentTime <= 12){
$('body').css('background-image', "url(" + randomImagePicker(images ,'breakfast') + ")");
}else if( currentTime > 12 && currentTime <= 16){
$('body').css('background-image', "url(" + randomImagePicker(images ,'lunch') + ")");
}else if( currentTime > 16 && currentTime <= 00){
$('body').css('background-image', "url(" + randomImagePicker(images ,'dinner') + ")");
}
}
function randomImagePicker(imgArray,time){
if(time == 'breakfast'){
return "../images/main_image/breakfast/" + imgArray[Math.floor(Math.random() * imgArray.length)];
}else if(time == 'lunch'){
return "../images/main_image/lunch/" + imgArray[Math.floor(Math.random() * imgArray.length)];
}else if(time == 'dinner'){
return "../images/main_image/dinner/" + imgArray[Math.floor(Math.random() * imgArray.length)];
}
}
Thank you.
Remove parens from the setInterval function argument. Now, this will invoke the function and set the return value of the function as the reference to setInterval which here is undefined as you don't return anything. So basically you are setting interval on nothing, so nothing happens except the first execution while setting up the setInterval.
Change
setInterval(uiImageChanger(),1); // This will invoke the function immediately.
to
setInterval(uiImageChanger,1); // You want to set the reference of the function to setInterval.
You have to pass a pointer to a function and not execute the function.
setInterval(uiImageChanger,1);
There are multiple ways of defining the function to be executed using setInterval.
One of the method is using the function reference for which the example is given by #mohkhan.
However you can do the following as well
setInterval(function(){
// code comes here.
}, time_in_mills);
Also I see that you have mentioned the value for the function execution as 1. This means every 1 millisecond the function will be executed, which is not a good practice at all. Give a realistic time in millisecond so that you have given sufficient time for the code to execute.
You've gotten several answers about the setInterval() problem. I'd like to point out a couple of other problems in the code.
First, this test will always fail:
else if( currentTime > 16 && currentTime <= 00)
After all, if a number is > 16 it cannot also be <= 0.
Also, you may get a warning about 00 being an octal constant which is deprecated. Of course, octal zero is the same value as decimal zero, but watch out for inadvertent octal constants: avoid using a leading zero.
And there is a lot of repetition in the code. You can easily remove all of this repetition to make the code more maintainable. Consider this approach:
// Return a random integer >= 0 and < n
function randomInt( n ) {
return Math.floor( Math.random() * n );
}
// Return a random element from an array
function randomElement( array ) {
return array[ randomInt(array.length) ];
}
function uiImageChanger(){
var hour = new Date().getHours();
var meal =
hour <= 12 ? 'breakfast' :
hour <= 16 ? 'lunch' :
'dinner';
var images = [ 'image1.jpg', 'image2.jpg' ];
$('body').css(
'background-image',
'url(../images/main_image/' + meal +
'/' + randomElement(images) + ')'
);
}

Categories