Simple issues with setInterval - javascript

so sorry for a basic qn like this. This is my simple JS code to do a timer. I was hoping to print the countdown number periodically. I tested the rest of the code and it seems to work, however it gives me sth weird when I add the setInterval command. I am not sure why. I hence seek an explanation and how to correct it.
Also, when it works, the new reloaded number should replace the old number right. For instance when 4 appears, it simply replaces 5 during the countdown.
Code:
var x = prompt("Time till take off");
function printTimer (){
document.write(x)
}
while (x > 0) {
setInterval(printTimer,1000)
x = x -1;
}
if (x=1){
document.write("Rocket taken off")}
Thanks!

The following snippet I think does what you're looking for. Further explanation is in the code comments for a little context:
var output = document.getElementById('count-down');
/**
* Initiates a countdown from given time in seconds
* #param {number} count
*/
function countDown(count) {
// Create the interval and save it in a variable 'interval'.
// We need it later when the countdown reaches 0
var interval = setInterval(function writeCount() {
// If time till takeoff is greater than 0, we print the current
// count and then decrement the count, so that next time count
// will be ( count - 1 )
if (count > 0) {
output.innerText = count;
count--;
// OR: count -= 1';
// OR: count = count - 1';
// are all equivalent
}
// Otherwise we write a final value, and clear the timeout
// so that we only get this final value once. If not cleared
// the next second count will be -1, and this else block would
// be re-executed.
else {
output.innerText = "Rocket taken off";
clearTimeout(interval);
}
}, 1000);
}
countDown(prompt("Time till take off"));
<div id="count-down"></div>

I think #jeffrey-westerkamp's solution is elegant and the way to go to achieve your result. However, if you are curious why your current code isn't working, here's what's happening.
There are a 3 big issues.
You are using setInterval instead of setTimeout.
setInterval will call a function repeatedly, until it is cancelled, at the specified time interval.
This would mean that if the rest of your code was working, it would output "10, 10, 10, 10, 10, 10, 10..." "9, 9, 9, 9, 9, 9, 9.." etc., until you cancelled each number's interval.
setTimeout calls the specified function once at the specified amount of time in the future.
You need to bind the variable x to your call to each setTimeout.
Right now, each time printTimer is called, it looks at the value of x. But here's the thing: the value of x is going to be 0 for each call.
Why? The while loop queues up all the calls to setTimeout (or in your case setInterval. At the specified time in the future, printTimer gets called. When it does, it looks for the variable x. By the time the first printTimer call runs, x has long since been set to zero from the while loop.
You need to make the delay at which setTimeout is called dependent on the position in the countdown sequence.
A for loop makes this a little bit more intuitive. Something like this:
function printTimer(count) {
if (count===1){ console.log("Rocket taken off"); }
else { console.log(count); }
}
for (var i=0;i<x;i++) {
(function(count){
setTimeout(printTimer.bind(null,x-count),1000*count);
})(i);
}
That strange syntax inside the for loop is called an IIFE, or an immediately invoked function expression. Another way to write that for loop without the IIFE would be using let instead of var:
for (let i=0;i<x;i++) { //also works
setTimeout(printTimer.bind(null,x-i),1000*i);
}
If this is confusing, check out this section of You Don't Know JS: Loops+Closure.

Related

Why the unexpected result showing the results?

function init(n) {
console.log(n);
setInterval(init.bind(this, ++n), 1000);
}
// init(0); // Works bad
function init2(n) {
setInterval(function() {
console.log(n++);
}, 1000);
}
init2(0); // Works fine
The first is a recursive function with to a setInterval.
Why does the first function NOT work as "expected"? with "expected" I mean that every 1 second is shown n + 1
I have been told that it is because by calling itself, calls are multiplying more and more and overflowing memory.
This I have noticed, but I can not make the mental image of this, can someone explain it to me more detailed? My problem is that I thought that, when executing the function every 1 second, I assured myself that exactly that would happen, but no. Calls multiply and do not wait for 1000 ms.
My mental image of the flow is:
1 - Call init ()
2 - Show n and then add 1 to n
3 - With a setInterval, execute the init () function only if 1000 ms have
passed.
4- 1 second passed ..
5- We return to step 1
Call init() manually. The function starts an interval that triggers once every second.
A second later, the interval triggers the first time and calls init() which starts another interval.
Two seconds later (counting from the start) both intervals trigger and call init(). Both function calls start a new interval. There are now 4 intervals in total.
Three seconds later the 4 intervals trigger and each start a new interval. There are now 8 intervals in total.
and so on until you run out of resources.
In init2() this doesn't happen because the interval never calls the function again and therefore there's ever only one interval running.
The first snippet is fine if you use setTimeout instead:
function init(n) {
if (n > 10) return; // 10 is enough in the example code
console.log(n);
setTimeout(init.bind(this, ++n), 1000);
}
init(0); // Works good
See, in your original case each call of init() runs setInterval - in other words, sets up a different task repeated each 1 sec. But you clearly need a single interval (as in the second snippet) - just continued at each step.
The problem is that init.bind(this, ++n) returns a new function and that new function then returns a new function, and so on. There isn't just one init function running every second. So, there are new copies being created every second and they are, in turn, making more copies. There isn't just one init running by itself. The copies are getting "stacked" up.

Javascript Loop: run function after precise time

I have searched high and low for a solution to this (that I can understand) and have yet to find one.
Fiddle here
https://jsfiddle.net/the_o/a7dwp41s/
The Goal:
To have the text change after a set period of time (for example 1 second).
The Problem:
The timing is not accurate at all. It's not apparent in the fiddle but on my page the text sometimes changes waaaay too fast. Also sometimes the loop will just stop. I know that setTimeout is not accurate from reading other Stack Overflow answers, but have not come across a good solution for running a function after a set period accurately. I'd appreciate some help.
The HTML:
<span class="text-center"><span class="top-line">A heading here</span>
<br><span class="bottom-line">There once was a...<br>
<span id="changeTextMobile"></span></span>
</span>
The Javascript:
var text = ["carrot", "potato", "tomato", "lettuce", "radish", "cabbage", "melon", "cucumber"];
var elem = document.getElementById("changeTextMobile");
var counter = 0;
function rotate_text() {
elem.innerHTML = text[counter];
if (counter < 8) {
counter++window.setTimeout(rotate_text, 1200);
}
if (counter == 8) {
counter = 0;
}
}
rotate_text();
Here's the fiddle again: https://jsfiddle.net/the_o/a7dwp41s/
The classic approach here is to set a fine-grained timer (either with setTimeout or more likely requestAnimationFrame, then within that examine the current time and compute the difference with the previous time, and execute your action if the desired amount of time has passed. RAF will send your callback a high-resolution timer which can be used if you are interested in sub-millisecond precision.
var text = ["carrot", "potato", ...];
var elem = document.getElementById("changeTextMobile");
var counter = 0;
var DELAY = 1000;
var old_timestamp = 0;
function rotate_text(timestamp) {
if (timestamp > old_timestamp + DELAY) { // HAVE WE WAITED LONG ENOUGH?
update_vegetable(); // CHANGE VEGETABLE NAME.
old_timestamp = timestamp; // REMEMBER WHEN WE DID THAT.
}
requestAnimationFrame(rotate_text); // RINSE AND REPEAT
}
rotate_text(0);
where update_vegetable would look something like
function update_vegetable() {
elem.textContent = text[counter++ % 8];
}
This should give you very accurate results. However, note that some browsers may slow down requestAnimationFrame when the tab is in the background. Also note that requestAnimationFrame may require vendor prefixing in certain browsers.
If you don't want to use requestAnimationFrame for some reason, you should be able to replace the call to it with setTimeout(tick, 16) with similar results.
Analysis of current code
In your current code, when counter reaches 8 and you reset it to 0, you are not calling setTimeout again to continue the sequence. That seems wrong.
if (counter == 8) {
counter = 0; //YOU ARE NOT RESETTING THE TIMEOUT.
}
In any case, you're better off using counter % 8 as another answer suggests and as shown above.
Also, the line below seems broken and is missing a semicolon. Is this your actual code? What it would do is add to counter the timer ID returned by setTimeout, which is completely meaningless.
counter++window.setTimeout(rotate_text, 1200);
should be
counter++;
window.setTimeout(rotate_text, 1200);
You should use setInterval for such cases. I have updated JSFiddle.
setTimeout
setTimeout is used for cases when you have to add a delay before a function is executed.
setInterval
setInterval is used for case where you have to run certain function after certain time delay.
Also, just a note, setTimeout(function(){},0) does not mean it will be executed immediately. When you use these function, an event is registered to be executed at certain tick, so setTimeout(function(){},0) will be triggered on next tick and not immediately.
var text = ["carrot", "potato", "tomato", "lettuce", "radish", "cabbage", "melon", "cucumber"];
var counter = 0;
function initInterval() {
interval = setInterval(function() {
counter++;
$("#changeTextMobile").text(text[counter % 8]);
}, 1200);
}
$(document).ready(function() {
initInterval();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<span class="text-center"><span class="top-line">A heading here</span>
<br><span class="bottom-line">There once was a...<br>
<span id="changeTextMobile"></span></span>
</span>
Hope it helps.
Another way to accomplish your goal would be to use the setInterval() method instead of the setTimeout() method.
Documentation for the method is found on W3Schools.com
Unless I am misunderstanding your intention, you don't just want to change your text once, but repeatedly. In this case, setInterval() is the choice as setTimeout() is intended to only run a function once. This could be the source of your consistency issues.
If and when you wish to stop the timer's repeat functionality, use clearInterval() in your trigger.

How to trace in the console how many intervals are being executed - JavaScript

I have a problem trying to know if a setInterval() is taking place or it was killed.
I am creating an interval and saving it into a variable:
interval = setInterval('rotate()',3000);
Then on click to an element I stop the interval and wait 10 seconds before starting a new one, by the way the variable interval is global:
$(elm).click(function(){
clearInterval(interval);
position = index;
$('#banner div').animate({
'margin-left':position*(-img_width)
});
setTimeout('startItnerval()',10000);
});
function startItnerval(){
interval = setInterval('rotate()',3000);
}
It seems to work but eventually I can realize that there are intervals still being in place, everytime I start a new interval it is saved in the interval variable, which is global, so in theory even if I start 100 intervals they are all saved in the same variable replacing the previous interval right? So I should only have one instance of interval; then on clearInterval(interval); it should stop any instance.
After looking at the results, apparently even if it is saved in the same variable, they are all separate instances and need to be killed individually.
How can I trace how many intervals are being executed, and if possible identify them one by one? even if I am able to solve the problem I really would like to know if there is a way to count or show in the console how many intervals are being executed?
thanks
jsFiddle Demo
As pointed out in comments, the id's constantly increase as timers are added to a page. As a result, it may be possible to clear all timers running on a page like this:
function clearTimers(){
var t = window.setTimeout(function(){
var idMax = t;
for( var i = 0; i < idMax; i++ ){
window.clearInterval(i);
window.clearTimeout(i);
}
},4);
}
The reason that you can only see one interval is because every time you start a new interval, you overwrite the value in interval. This causes the previous intervals to be lost but still active.
A suggestion would be to just control access to your variable. Clearly there is an issue where the start function is called too often
clearInterval(interval);//when you clear it, null it
interval = null;
and then take advantage of that later
if( interval != null ){
interval = setInterval('rotate()',3000);
}
Also, as Pointy noted in a comment, using a string to call a function is not best practice. What it basically does is converts it into a Function expression which is similar to using eval. You should probably either use the function name as a callback
setInterval(rotate,3000);
or have an anonymous function issue the callback
setInterval(function(){ rotate(); },3000);
setInterval returns an Id, not the actual object, so no, no interval will be overriden if you repeat the line
var xy = setInterval(function() {...}, 1000);
If you want to stop the interval you have to clear it:
clearInterval(xy);
And if your startInterval can be called multiple times in a row, but you don't want to create multiple intervals, just clear the inverval before you start a new one:
function startInterval(){
clearInterval(interval);
interval = setInterval('rotate()',3000);
}
If you have to create multiple intervals, you could save the ids in an array to keep track of them:
var arr = [];
//set the interval
arr.push(setInterval(...));
//get number of currently running intervals
var count = arr.length //gives you the number of currently running intervals
//clear the interval with index i
clearInterval(arr[i]);
arr.splice(i, 1);

Trying to display multiple count down timers on same page

I am trying to display several count down timers on same page. now as far as i know there are 2 ways of doing it without using jquery plugins or some other scripts (if you know of a good one please let me know)
starting 1 sec setInterval and a global variable that will contain milliseconds and then just reduce -1000 every interval.
creating a function that reduce 1 sec from a global variable and then at the bottom of that function setting a setTimeout of 1 sec that will run that functions so basically recursion every 1 sec.
My question is which of the 2 options will work better and/or faster?
here is demonstrative code for both:
setInterval:
var amount_of_seconds_left = 46800000;
setInterval(function(){
if(amount_of_seconds_left > 1000){
amount_of_seconds_left -= 1000;
}
},1000);
setTimeout:
var amount_of_seconds_left = 46800000;
function startTime(){
if(amount_of_seconds_left > 1000){
amount_of_seconds_left -= 1000;
t=setTimeout(function(){startTime()},1000);
}
}
Both ways could work but i was wondering performance wise which is better and is performance is even an issue with this ?
setInterval and setTimeout don't start after 1000ms e.g. if another script is running, so both can cause delays. It would be better to use the setIntervall to call the display update only and use the the Date object to calculate the exactly remaining time. E.g. after the browser was busy the timer shows the correct time after the next update.
Here an example:
HTML:
<div id="timer1"></div>
<div id="timer2"></div>
javascript:
// update all timer
function updateTimer() {
for (var i in aTimer) {
var oTimer = document.getElementById(aTimer[i].sId);
var iSeconds = parseInt((aTimer[i].iFinished - Date.now()) / 1000);
oTimer.innerHTML = iSeconds;
}
}
// Init all timers with DOM-id and finish time
var aTimer = [
{ sId: 'timer1', iFinished: Date.now() + 46800000 },
{ sId: 'timer2', iFinished: Date.now() + 780000}
];
// call display update
setInterval(function() {
updateTimer();
}, 333);
I belive that the setInterval code executes every 1000ms exactly, while the setTimeout waits 1000ms, runs the function, which takes some ms, then sets another timeout. So the wait period is actually greater than 1000ms.
From this post:
setTimeout or setInterval?

Add a countdown in my scoring script (javascript/jquery)

I have the following script in a js file:
// Ad score
var score = 0;
//$('#score').text(score);
function foundMatchingBlocks(event, params) {
params.elements.remove();
score += 100;
$('#score').text(score);
};
Now on each matching, 100 points are added to var score. This all works. Now I want to extend this a bit. As soon as the page loads I want to start a countdown to reduce the number of points (starting with 100) with 1 point a second for 60 seconds. So the minimum number of points a user can get is 40. When someone gets the points, the counter should reset and countdown again.
Example:
Page loads (timer starts from 100)
User has a match after 10 seconds (+90 points are added)
Counter resets and countdown from 100 again
User found a match after 35 sec (+65 points are added)
etc etc
Problem is, I have no idea how to do this :( Hope someone can help me with this.
The above is fixed, thanks all for helping!!!
The big picture is, you'll need to become pretty familiar with timeouts and intervals in javascript. This is the reference page I keep going back to when I need to refresh my memory: http://www.elated.com/articles/javascript-timers-with-settimeout-and-setinterval/
For your specific task, you'll probably want to use an Interval that triggers every 1000 milliseconds to calculate the second-by-second point reduction, and a separate Timeout for failure that resets every time the user completes their challenge.
Here are a few tips for working with timeouts and intervals that usually lead to followup questions:
When you set a timeout, always capture the return value (I think it's basically a random integer). Save it to some global var for convenience.
var failureTimer; // global var high up in your scope
failureTimer = setTimeout ( "gameOver()", 100000 ); // 100 seconds * 1000ms
Then in whichever method gets called when the player completes their challenge, you call this:
clearTimeout (failureTimer); // resets the timer and gives them another 100 seconds
failureTimer = setTimeout ( "gameOver()", 100000 ); // yes call this again, to start the 100 sec countdown all over again.
The second pain point you're likely to encounter when working with Timeouts and Intervals is how to pass parameters to the functions like gameOver() in my example above. You have to use anonymous functions, as described here:
Pass parameters in setInterval function
For more on anonymous functions, this is a good overview:
http://helephant.com/2008/08/23/javascript-anonymous-functions/
Good luck with your project! Let me know if you have any questions.
Here's some code without the use of timers. Call startCountdown() every time you want to re-initialize the count-down. Call getAvailableScore() when you want to fetch the current available score. You will have to decide what to do when the available score goes to zero.
var beginCountDownTime;
function startCountdown() {
beginCountDownTime = new Date();
}
function getAvailableScore {
var now = new Date();
var delta = (now.getTime() - beginCountDownTime.getTime()) * 1000; // time elapsed in seconds
var points = 100 - (delta / 60);
return(Math.round(Math.max(points, 0))); // return integer result >= 0
}
Maybe something like:
// Ad score
var score = 0;
var pointsAvailable = 100;
//$('#score').text(score);
function foundMatchingBlocks(event, params) {
params.elements.remove();
score += pointsAvailable;
$('#score').text(score);
pointsAvailable = 100;
};
$(document).ready(function() {doTimer();});
function doTimer() {
setTimeout('reducePoints()',1000);
}
function reducePoints() {
if(pointsAvailable>40) {
pointsAvailable--;
}
doTimer();
}

Categories

Resources