Why does my javascript countdown timer occasionally over-run? - javascript

As part of a web-based educational game, I have a countdown timer using the code that follows. Mostly, the timer stops at zero, but occasionally, it over-runs, and continues to count down from 59:59.
A couple of notes about the code:
countTime is a global variable set by a dropdown menu
stopCount is a gloabal variable set by a reset button
leadZero is an external function used for formatting
I understand that setTimeout is not very accurate, but I would have thought that the remTime > 0 condition would stop the recursion eventually, even if it missed the first time.
Anyway, here's the code:
function newCount() {
var startTime=new Date();
var min=startTime.getMinutes();
var sec=startTime.getSeconds();
var endTime=(min*60)+sec+countTime;
stopCount=false;
countDown();
function countDown() {
var today=new Date();
var m=today.getMinutes();
var s=today.getSeconds();
var currTime=(m*60)+s;
var remTime=endTime-currTime;
var remMin = Math.floor(remTime/60);
var remSec = remTime % 60;
// add a zero in front of numbers<10
remMin=leadZero(remMin);
remSec=leadZero(remSec);
document.getElementById('timer').innerHTML=remMin+":"+remSec;
if (remTime > 0 && stopCount==false) {
t=setTimeout(function(){countDown()},500);
}
else if (stopCount==false){document.getElementById("nextButton").innerHTML = "Finished";}
else {}
}
}
As requested, here is the code for the buttons and calling functions ...
Buttons:
<button onclick="newSyllable()" id="nextButton" style="font:60px 'Nunito', sans-serif;">Start</button>
<button onclick="resetScore()"><span style="font:20px 'Nunito', sans-serif;">Reset</span></button>
Functions:
function resetScore() {
points=0
stopCount=true;
document.getElementById("score").innerHTML = "Score: " + points
document.getElementById("nextButton").innerHTML = "Start"
document.getElementById("syllable").innerHTML = " "
t=setTimeout(function(){setCountDown()},500);
}
function newSyllable() {
if (document.getElementById("nextButton").innerHTML == "Finished"){
}
else {
if (document.getElementById("nextButton").innerHTML == "Start"){
newCount();
}
document.getElementById("nextButton").innerHTML = "Next"
switch (currentUnit) {
case "1":
unit1();
break;
case "2":
unit2();
break;
case "3":
unit3();
break;
case "4":
unit4();
break;
case "5":
unit5();
break;
case "6":
unit6();
break;
}
document.getElementById("score").innerHTML = "Score: " + points++
}
}

Ok so I think the problem originates from the way you're calculating the remaining time by using two Date objects. A much simpler way to do this would be to just use your countTime variable as the starting time (in seconds), and then use a 1000 millisecond interval to perform the countdown. Try this code instead:
var stopCount = false;
var countTime = 10;
function newCount() {
if(stopCount === false) {
var counter=setInterval(countDown, 1000);
}
stopCount = true;
function countDown() {
countTime = countTime - 1;
var remMin = Math.floor(countTime/60);
var remSec = countTime % 60;
// add a zero in front of numbers<10
remMin=leadZero(remMin);
remSec=leadZero(remSec);
document.getElementById('timer').innerHTML=remMin+":"+remSec;
if (countTime <= 0)
{
clearInterval(counter);
document.getElementById("nextButton").innerHTML = "Finished";
return;
}
}
}
JSFiddle: http://jsfiddle.net/5vxbe/8/

I simplified the code further, using the countTime = countTime-1 idea from #JL, but using my original setTimeout recursive function rather than setInterval and clearInterval.
I want the "reset" button to be able to stop and reset the timer before it's finished, and this code made that a bit simpler (I think).
Here's the final code:
function newCount() {
stopCount=false;
countDown();
function countDown() {
if (stopCount==false) {
countTime=countTime-1
var remMin = Math.floor(countTime/60);
var remSec = countTime % 60;
// add a zero in front of numbers<10
remMin=leadZero(remMin);
remSec=leadZero(remSec);
document.getElementById('timer').innerHTML=remMin+":"+remSec;
if (countTime > 0) {
t=setTimeout(function(){countDown()},1000);
}
else {document.getElementById("nextButton").innerHTML = "Finished";}
}
}
}

Related

Can't use variable as setInterval delay? [duplicate]

I have written a javascript function that uses setInterval to manipulate a string every tenth of a second for a certain number of iterations.
function timer() {
var section = document.getElementById('txt').value;
var len = section.length;
var rands = new Array();
for (i=0; i<len; i++) {
rands.push(Math.floor(Math.random()*len));
};
var counter = 0
var interval = setInterval(function() {
var letters = section.split('');
for (j=0; j < len; j++) {
if (counter < rands[j]) {
letters[j] = Math.floor(Math.random()*9);
};
};
document.getElementById('txt').value = letters.join('');
counter++
if (counter > rands.max()) {
clearInterval(interval);
}
}, 100);
};
Instead of having the interval set at a specific number, I would like to update it every time it runs, based on a counter. So instead of:
var interval = setInterval(function() { ... }, 100);
It would be something like:
var interval = setInterval(function() { ... }, 10*counter);
Unfortunately, that did not work. It seemed like "10*counter" equals 0.
So, how can I adjust the interval every time the anonymous function runs?
You could use an anonymous function:
var counter = 10;
var myFunction = function(){
clearInterval(interval);
counter *= 10;
interval = setInterval(myFunction, counter);
}
var interval = setInterval(myFunction, counter);
UPDATE: As suggested by A. Wolff, use setTimeout to avoid the need for clearInterval.
var counter = 10;
var myFunction = function() {
counter *= 10;
setTimeout(myFunction, counter);
}
setTimeout(myFunction, counter);
Use setTimeout() instead. The callback would then be responsible for firing the next timeout, at which point you can increase or otherwise manipulate the timing.
EDIT
Here's a generic function you can use to apply a "decelerating" timeout for ANY function call.
function setDeceleratingTimeout(callback, factor, times)
{
var internalCallback = function(tick, counter) {
return function() {
if (--tick >= 0) {
window.setTimeout(internalCallback, ++counter * factor);
callback();
}
}
}(times, 0);
window.setTimeout(internalCallback, factor);
};
// console.log() requires firebug
setDeceleratingTimeout(function(){ console.log('hi'); }, 10, 10);
setDeceleratingTimeout(function(){ console.log('bye'); }, 100, 10);
I like this question - inspired a little timer object in me:
window.setVariableInterval = function(callbackFunc, timing) {
var variableInterval = {
interval: timing,
callback: callbackFunc,
stopped: false,
runLoop: function() {
if (variableInterval.stopped) return;
var result = variableInterval.callback.call(variableInterval);
if (typeof result == 'number')
{
if (result === 0) return;
variableInterval.interval = result;
}
variableInterval.loop();
},
stop: function() {
this.stopped = true;
window.clearTimeout(this.timeout);
},
start: function() {
this.stopped = false;
return this.loop();
},
loop: function() {
this.timeout = window.setTimeout(this.runLoop, this.interval);
return this;
}
};
return variableInterval.start();
};
Example use
var vi = setVariableInterval(function() {
// this is the variableInterval - so we can change/get the interval here:
var interval = this.interval;
// print it for the hell of it
console.log(interval);
// we can stop ourselves.
if (interval>4000) this.stop();
// we could return a new interval after doing something
return interval + 100;
}, 100);
// we can change the interval down here too
setTimeout(function() {
vi.interval = 3500;
}, 1000);
// or tell it to start back up in a minute
setTimeout(function() {
vi.interval = 100;
vi.start();
}, 60000);
I had the same question as the original poster, did this as a solution. Not sure how efficient this is ....
let interval = 5000; // initial condition
let run = setInterval(request, interval); // start setInterval as "run"
function request() {
console.log(interval); // firebug or chrome log
clearInterval(run); // stop the setInterval()
// dynamically change the run interval
if (interval > 200) {
interval = interval * .8;
} else {
interval = interval * 1.2;
}
run = setInterval(request, interval); // start the setInterval()
}
This is my way of doing this, i use setTimeout:
var timer = {
running: false,
iv: 5000,
timeout: false,
cb : function(){},
start : function(cb,iv){
var elm = this;
clearInterval(this.timeout);
this.running = true;
if(cb) this.cb = cb;
if(iv) this.iv = iv;
this.timeout = setTimeout(function(){elm.execute(elm)}, this.iv);
},
execute : function(e){
if(!e.running) return false;
e.cb();
e.start();
},
stop : function(){
this.running = false;
},
set_interval : function(iv){
clearInterval(this.timeout);
this.start(false, iv);
}
};
Usage:
timer.start(function(){
console.debug('go');
}, 2000);
timer.set_interval(500);
timer.stop();
A much simpler way would be to have an if statement in the refreshed function and a control to execute your command at regular time intervals . In the following example, I run an alert every 2 seconds and the interval (intrv) can be changed dynamically...
var i=1;
var intrv=2; // << control this variable
var refreshId = setInterval(function() {
if(!(i%intrv)) {
alert('run!');
}
i++;
}, 1000);
This can be initiated however you want. timeout is the method i used to keep it on the top of the hour.
I had the need for every hour to begin a code block on the hour. So this would start at server startup and run the interval hourly. Basicaly the initial run is to begin the interval within the same minute. So in a second from init, run immediately then on every 5 seconds.
var interval = 1000;
var timing =function(){
var timer = setInterval(function(){
console.log(interval);
if(interval == 1000){ /*interval you dont want anymore or increment/decrement */
interval = 3600000; /* Increment you do want for timer */
clearInterval(timer);
timing();
}
},interval);
}
timing();
Alternately if you wanted to just have something happen at start and then forever at a specific interval you could just call it at the same time as the setInterval. For example:
var this = function(){
//do
}
setInterval(function(){
this()
},3600000)
this()
Here we have this run the first time and then every hour.
I couldn't synchronize and change the speed my setIntervals too and I was about to post a question. But I think I've found a way. It should certainly be improved because I'm a beginner. So, I'd gladly read your comments/remarks about this.
<body onload="foo()">
<div id="count1">0</div>
<div id="count2">2nd counter is stopped</div>
<button onclick="speed0()">pause</button>
<button onclick="speedx(1)">normal speed</button>
<button onclick="speedx(2)">speed x2</button>
<button onclick="speedx(4)">speed x4</button>
<button onclick="startTimer2()">Start second timer</button>
</body>
<script>
var count1 = 0,
count2 = 0,
greenlight = new Boolean(0), //blocks 2nd counter
speed = 1000, //1second
countingSpeed;
function foo(){
countingSpeed = setInterval(function(){
counter1();
counter2();
},speed);
}
function counter1(){
count1++;
document.getElementById("count1").innerHTML=count1;
}
function counter2(){
if (greenlight != false) {
count2++;
document.getElementById("count2").innerHTML=count2;
}
}
function startTimer2(){
//while the button hasn't been clicked, greenlight boolean is false
//thus, the 2nd timer is blocked
greenlight = true;
counter2();
//counter2() is greenlighted
}
//these functions modify the speed of the counters
function speed0(){
clearInterval(countingSpeed);
}
function speedx(a){
clearInterval(countingSpeed);
speed=1000/a;
foo();
}
</script>
If you want the counters to begin to increase once the page is loaded, put counter1() and counter2() in foo() before countingSpeed is called. Otherwise, it takes speed milliseconds before execution.
EDIT : Shorter answer.
(function variableInterval() {
//whatever needs to be done
interval *= 2; //deal with your interval
setTimeout(variableInterval, interval);
//whatever needs to be done
})();
can't get any shorter
Here is yet another way to create a decelerating/accelerating interval timer. The interval gets multiplied by a factor until a total time is exceeded.
function setChangingInterval(callback, startInterval, factor, totalTime) {
let remainingTime = totalTime;
let interval = startInterval;
const internalTimer = () => {
remainingTime -= interval ;
interval *= factor;
if (remainingTime >= 0) {
setTimeout(internalTimer, interval);
callback();
}
};
internalTimer();
}
Make new function:
// set Time interval
$("3000,18000").Multitimeout();
jQuery.fn.extend({
Multitimeout: function () {
var res = this.selector.split(",");
$.each(res, function (index, val) { setTimeout(function () {
//...Call function
temp();
}, val); });
return true;
}
});
function temp()
{
alert();
}
This piece of code below accelerates (acceleration > 1) or decelerates (acceleration <1) a setInterval function :
function accelerate(yourfunction, timer, refresh, acceleration) {
var new_timer = timer / acceleration;
var refresh_init = refresh;//save this user defined value
if (refresh < new_timer ){//avoid reseting the interval before it has produced anything.
refresh = new_timer + 1 ;
};
var lastInter = setInterval(yourfunction, new_timer);
console.log("timer:", new_timer);
function stopLastInter() {
clearInterval(lastInter);
accelerate(yourfunction, new_timer, refresh_init, acceleration);
console.log("refresh:", refresh);
};
setTimeout(stopLastInter, refresh);
}
With :
timer: the setInterval initial value in ms (increasing or decreasing)
refresh: the time before a new value of timer is calculated. This is the step lenght
acceleration: the gap between the old and the next timer value. This is the step height
Inspired by the internal callback above, i made a function to fire a callback at fractions of minutes. If timeout is set to intervals like 6 000, 15 000, 30 000, 60 000 it will continuously adapt the intervals in sync to the exact transition to the next minute of your system clock.
//Interval timer to trigger on even minute intervals
function setIntervalSynced(callback, intervalMs) {
//Calculate time to next modulus timer event
var betterInterval = function () {
var d = new Date();
var millis = (d.getMinutes() * 60 + d.getSeconds()) * 1000 + d.getMilliseconds();
return intervalMs - millis % intervalMs;
};
//Internal callback
var internalCallback = function () {
return function () {
setTimeout(internalCallback, betterInterval());
callback();
}
}();
//Initial call to start internal callback
setTimeout(internalCallback, betterInterval());
};
This is my idea for times when you do not want loops like setInterval to overlap.
You also want to be able to set the loop execution delay and start and stop the loop, instansly on the fly.
I am using a loop_flag variable and a setTimeout function.
I set the main function to async so that you can call other functions in the body by calling await. When the main body of your code is running, the main loop waits and does not repeat itself. (which is not the case with setInterval)
An example of a simple code is:
//#NabiKAZ
document.getElementById("btn_start").addEventListener("click", function() {
console.log("Starting...");
loop_flag = true;
loop_func();
});
document.getElementById("btn_stop").addEventListener("click", function() {
console.log("Stoping...");
loop_flag = false;
});
var n = 0;
var loop_flag = false;
var loop_func = async function() {
if (!loop_flag) {
console.log("STOP.");
return;
}
//body main function inhere
n++;
console.log(n);
////
if (loop_flag) {
setTimeout(loop_func, document.getElementById("inp_delay").value);
} else {
console.log("STOP.");
}
}
<input id="inp_delay" value="1000">
<button id="btn_start">START</button>
<button id="btn_stop">STOP</button>
For a more complete code with a fetch request inside the loop, see here:
https://jsfiddle.net/NabiKAZ/a5hdw2bo/
You can use a variable and change the variable instead.
setInterval(() => function, variable)
You can do this by clearing the interval every iteration, changing the timer value and setting the interval again. Hope it helps ;)
For exemple:
const DOMCounter = document.querySelector(".counter")
let timer = 1000
const changeCounter = () => {
clearInterval(interval)
DOMCounter.innerHTML = timer
timer += 1000
timer == 5000 && timer == 1000
interval = setInterval(changeCounter, timer)
}
let interval = setInterval(changeCounter, timer)
<div class="container">
<p class="counter"></p>
</div>
var counter = 15;
var interval = function() {
setTimeout(function(){
// Write your code here and remove console.log, remember that you need declare yourDynamicValue and give it a value
console.log((new Date()).getTime())
window.counter = yourDynamicValue;
window.interval();
}, counter);
}
// It needs to run just once as init
interval();

javascript change background and play sound with one button

I'm building a clock inspired from the link below with added features like the single party button will play a music and change the BodyBGcolor per second.
https://codepen.io/codifiedconcepts/pen/bwgxRq
The music plays and pauses fine, but the BG starts changing but doesn't stop if I click to pause, and starts flickering and gets faster if I click the button again.
var partyTime = false;
var catMusic = new Audio("media/party.mp3");
function partyEvent() {
// partyTime and catMusic are defined outside
// otherwise they get re-defined without changing the old definition
if (partyTime === false) {
partyTime = true;
catMusic.play();
partyBtn.innerHTML = "PARTY OVER";
} else {
partyTime = false;
catMusic.pause();
partyBtn.innerHTML = "PARTY TIME";
}
}
var i = 0;
function changeBG() {
var color = ["red", "blue", "brown", "green"];
document.body.style.backgroundColor = color[i];
i = (i + 1) % color.length;
if (partyTime === true) {
var initBG = setInterval(changeBG, 1000);
} else {
clearInterval(initBG);
}
}
I have another function which changes the BGcolor 5 times in a day, for morning, noon, evening etc.. as a switch statement, I'm showing one case here..
switch (true) {
case hour <= 5:
imgTxt.innerHTML = "GET SOME SLEEPZ BRO";
catImg.style.background = "url('img/earlyMorning.jpg')";
catImg.style.backgroundSize = "cover";
document.body.style.backgroundColor= "#2d3037";
clockBody.style.color = "#99ffcc";
break;
please help this noob, and sorry for the indentation, it gets all same after pasting here..
if (partyTime === true) {
var initBG = setInterval(changeBG, 1000);
} else {
clearInterval(initBG);
}
Here you are creating initBG local to if block. In the else block initBG is undefined so it does not stop the interval process.
Solution: Define initBG outside the partyEvent function.
var initBG;
if (partyTime === true) {
initBG = setInterval(changeBG, 1000);
} else {
clearInterval(initBG);
}

Javascript Pomodoro Timer - Pause/Play/General Functionality

so I seem to be having some real logic errors amidst my timer here, and I'm not quite sure how to fix them. I've made the code somewhat work, but I know for a fact there's a handful of logical errors, and I honestly have been working on it for a few days intermittently and can't seem to get it solved. Based on the code I've laid out you can pretty much understand my thinking of how I want to tackle this issue. I understand this isn't best practice, but I am just trying to work on my problem solving on these issues! Any help is greatly appreciated. I really need the most help on figuring out how to configure the Pause and Play functions with my given timer functionality. (Please no jQuery examples only raw JS)
// Necessary Variables for the program
let timer = document.querySelector(".time");
let button = document.querySelector("#btn");
let subtractBreak = document.querySelector("#subtractBreak");
let addBreak = document.querySelector("#addBreak");
let subtractSession = document.querySelector("#subtractSesssion");
let addSession= document.querySelector("#addSession");
// Keeping up with the current count of the break and session
let currentCount = document.getElementsByClassName("counterNum");
// Keeps up with all the buttons on session and break +/-
let counterBtn = document.getElementsByClassName("counterBtn");
// Pause and play Variables
let pause = document.querySelector("#pause");
let play = document.querySelector("#play");
// keeps up with an offsetting count.
let count = 0;
let timerGoing = false;
let sessionTimeLeft = 1500;
let breakTimeLeft = 5;
let timeWhilePaused = sessionTimeLeft;
let paused = false;
function formatTimer(time){
let minutes = Math.floor(time / 60);
let seconds = time % 60;
return `${minutes.toLocaleString(undefined, {minimumIntegerDigits: 2})}:${seconds.toLocaleString(undefined, {minimumIntegerDigits: 2})}`;
}
// This function needs to be fixed - It allows the timer to go negative past 0.
function countdown(){
timerGoing = true;
count++;
timer.innerHTML = formatTimer(sessionTimeLeft - count);
// Used to remove the listener
button.removeEventListener("click", interval);
console.log("Timer is at: " + formatTimer(sessionTimeLeft - count) + "\nCount is at: " + count + "\nsessionTimeLeft = " + sessionTimeLeft);
// Checking if the time of the current session is up
if(count == sessionTimeLeft){
timerGoing = false;
alert("We're here stop...");
startBreak(breakTimeLeft);
}
}
button.addEventListener("click", interval);
function interval(e){
setInterval(countdown, 1000);
console.log("running");
e.preventDefault();
}
/*
I look through the event listener to be sure to check and see if any of them have been hit
not just the first one, which is what it would check for.
*/
for(let i = 0; i < counterBtn.length; i++){
counterBtn[i].addEventListener("click", changeCount);
}
/*
This functions whole job is to see which button was clicked using the 'event target'
Once found it can determine if the id is subtract - then it will subtract the next element
founds' amount, due to the structure of the HTML this will always be the number following;
this process works vice versa for the addition, with the number being the element before.
*/
function changeCount(e){
let clickedButtonsId = e.target.id;
if(timerGoing == false){
if(clickedButtonsId === "subtractBreak"){
let currentValue = e.target.nextElementSibling.innerText;
if(currentValue != 1){
currentValue--;
e.target.nextElementSibling.innerText = currentValue;
breakTimeLeft -= 1;
}
} else if(clickedButtonsId === "subtractSession"){
let currentValue = e.target.nextElementSibling.innerText;
if(currentValue != 1){
currentValue--;
e.target.nextElementSibling.innerText = currentValue;
sessionTimeLeft = currentValue * 60;
// Allows the timer to update in real time
timer.innerHTML = formatTimer(sessionTimeLeft);
}
}
else if(clickedButtonsId === "addBreak"){
let currentValue = e.target.previousElementSibling.innerText;
currentValue++;
e.target.previousElementSibling.innerText = currentValue;
breakTimeLeft += 1;
}
else{
let currentValue = e.target.previousElementSibling.innerText;
currentValue++;
e.target.previousElementSibling.innerText = currentValue;
sessionTimeLeft = currentValue * 60;
// Allows the timer to update in real time
timer.innerHTML = formatTimer(sessionTimeLeft);
}
}
}
/* These functions below are not working */
pause.addEventListener("click", pauseTimer);
function pauseTimer(){
timeWhilePaused = sessionTimeLeft;
button.removeEventListener("click", interval);
console.log("calling pause"+paused+"\n");
paused = true;
console.log("paused after: " + paused);
}
play.addEventListener("click", playTimer);
function playTimer(){
console.log("Paused = " + paused);
if(paused == true){
console.log("calling play");
console.log("Paused = " + paused);
sessionTimeLeft = timeWhilePaused;
setInterval(countdown, 1000);
}
}
function startBreak(time){
console.log("Calling this function");
timer.innerHTML = formatTimer(breakTimeLeft - count);
}

How to get clearInterval() to work in a loop in JavaScript

So I'm trying to get a function to run once every second, and then after four seconds I want it to stop using clearInterval()
function dotdotdot(){
var x = 0;
setInterval(function(){
if (x>=3){
torpWri = torpWri + ".";
document.getElementById("torpTxt").innerHTML = torpWri;
x++;
}
else{
x = 0;
clearInterval();
}
},1000);
}
This is my function and it should stop after four seconds and then reset x to 0 for when I call it again.
function loadButton(){
torpWri = "Torpedo Loading"
if(torpLoadAmount[arNum]<5){
torpLoadAmount[arNum]++;
torpAmount--;
document.getElementById("torpCnt").innerHTML = torpAmount;
document.getElementById("torpTxt").style.visibility = "visible";
document.getElementById("butunload").disabled=true;
document.getElementById("butfire").disabled=true;
document.getElementById("torpTxt").innerHTML = torpWri;
dotdotdot();
}
else{
document.getElementById("torpTxt").style.visibility = "visible";
document.getElementById("torpTxt").innerHTML = "Torpedo Bay Full";
}
timer3();
}
This is how I'm calling it.
I'm just needed to know why it isn't running the function dotdotdot(); every second and then stopping after four. Then when I call it again it should all just reset. But it's not running...
I've been searching for a while and haven't found anything, so I came here.
(Also, please don't comment on my other code, I know there are probably easier ways to do it, but this is what I'm working with right now.)
setInterval returns a timerID, which needs to be passed to clearInterval.
var ticks = 0;
var intervalID = setInterval(function() {
if (++ticks == 4) {
clearInterval(intervalID);
}
}, 1000);
You could also use setTimeout instead, and just not schedule a new tick when the condition is met.
setTimeout(function callback(ticks) {
if (ticks > limit) {
return;
}
setTimeout(callback, 0, ++ticks);
}, 1000, 0)
You need to store the handle / intervalId for the interval when it is set and then use it when you want to clear the interval:
function dotdotdot(){
var x = 0;
var intervalId = -1;
intervalId = setInterval(function(){
if (x>=3){
torpWri = torpWri + ".";
document.getElementById("torpTxt").innerHTML = torpWri;
x++;
} else {
x = 0;
clearInterval(intervalId);
}
},1000);
}
More info: https://developer.mozilla.org/en-US/Add-ons/Code_snippets/Timers
setInterval will return a timerid. So do like
var timer = setInterval(fun......)
Then
clearInterval(timer)

Changing the interval of SetInterval while it's running

I have written a javascript function that uses setInterval to manipulate a string every tenth of a second for a certain number of iterations.
function timer() {
var section = document.getElementById('txt').value;
var len = section.length;
var rands = new Array();
for (i=0; i<len; i++) {
rands.push(Math.floor(Math.random()*len));
};
var counter = 0
var interval = setInterval(function() {
var letters = section.split('');
for (j=0; j < len; j++) {
if (counter < rands[j]) {
letters[j] = Math.floor(Math.random()*9);
};
};
document.getElementById('txt').value = letters.join('');
counter++
if (counter > rands.max()) {
clearInterval(interval);
}
}, 100);
};
Instead of having the interval set at a specific number, I would like to update it every time it runs, based on a counter. So instead of:
var interval = setInterval(function() { ... }, 100);
It would be something like:
var interval = setInterval(function() { ... }, 10*counter);
Unfortunately, that did not work. It seemed like "10*counter" equals 0.
So, how can I adjust the interval every time the anonymous function runs?
You could use an anonymous function:
var counter = 10;
var myFunction = function(){
clearInterval(interval);
counter *= 10;
interval = setInterval(myFunction, counter);
}
var interval = setInterval(myFunction, counter);
UPDATE: As suggested by A. Wolff, use setTimeout to avoid the need for clearInterval.
var counter = 10;
var myFunction = function() {
counter *= 10;
setTimeout(myFunction, counter);
}
setTimeout(myFunction, counter);
Use setTimeout() instead. The callback would then be responsible for firing the next timeout, at which point you can increase or otherwise manipulate the timing.
EDIT
Here's a generic function you can use to apply a "decelerating" timeout for ANY function call.
function setDeceleratingTimeout(callback, factor, times)
{
var internalCallback = function(tick, counter) {
return function() {
if (--tick >= 0) {
window.setTimeout(internalCallback, ++counter * factor);
callback();
}
}
}(times, 0);
window.setTimeout(internalCallback, factor);
};
// console.log() requires firebug
setDeceleratingTimeout(function(){ console.log('hi'); }, 10, 10);
setDeceleratingTimeout(function(){ console.log('bye'); }, 100, 10);
I like this question - inspired a little timer object in me:
window.setVariableInterval = function(callbackFunc, timing) {
var variableInterval = {
interval: timing,
callback: callbackFunc,
stopped: false,
runLoop: function() {
if (variableInterval.stopped) return;
var result = variableInterval.callback.call(variableInterval);
if (typeof result == 'number')
{
if (result === 0) return;
variableInterval.interval = result;
}
variableInterval.loop();
},
stop: function() {
this.stopped = true;
window.clearTimeout(this.timeout);
},
start: function() {
this.stopped = false;
return this.loop();
},
loop: function() {
this.timeout = window.setTimeout(this.runLoop, this.interval);
return this;
}
};
return variableInterval.start();
};
Example use
var vi = setVariableInterval(function() {
// this is the variableInterval - so we can change/get the interval here:
var interval = this.interval;
// print it for the hell of it
console.log(interval);
// we can stop ourselves.
if (interval>4000) this.stop();
// we could return a new interval after doing something
return interval + 100;
}, 100);
// we can change the interval down here too
setTimeout(function() {
vi.interval = 3500;
}, 1000);
// or tell it to start back up in a minute
setTimeout(function() {
vi.interval = 100;
vi.start();
}, 60000);
I had the same question as the original poster, did this as a solution. Not sure how efficient this is ....
let interval = 5000; // initial condition
let run = setInterval(request, interval); // start setInterval as "run"
function request() {
console.log(interval); // firebug or chrome log
clearInterval(run); // stop the setInterval()
// dynamically change the run interval
if (interval > 200) {
interval = interval * .8;
} else {
interval = interval * 1.2;
}
run = setInterval(request, interval); // start the setInterval()
}
This is my way of doing this, i use setTimeout:
var timer = {
running: false,
iv: 5000,
timeout: false,
cb : function(){},
start : function(cb,iv){
var elm = this;
clearInterval(this.timeout);
this.running = true;
if(cb) this.cb = cb;
if(iv) this.iv = iv;
this.timeout = setTimeout(function(){elm.execute(elm)}, this.iv);
},
execute : function(e){
if(!e.running) return false;
e.cb();
e.start();
},
stop : function(){
this.running = false;
},
set_interval : function(iv){
clearInterval(this.timeout);
this.start(false, iv);
}
};
Usage:
timer.start(function(){
console.debug('go');
}, 2000);
timer.set_interval(500);
timer.stop();
A much simpler way would be to have an if statement in the refreshed function and a control to execute your command at regular time intervals . In the following example, I run an alert every 2 seconds and the interval (intrv) can be changed dynamically...
var i=1;
var intrv=2; // << control this variable
var refreshId = setInterval(function() {
if(!(i%intrv)) {
alert('run!');
}
i++;
}, 1000);
This can be initiated however you want. timeout is the method i used to keep it on the top of the hour.
I had the need for every hour to begin a code block on the hour. So this would start at server startup and run the interval hourly. Basicaly the initial run is to begin the interval within the same minute. So in a second from init, run immediately then on every 5 seconds.
var interval = 1000;
var timing =function(){
var timer = setInterval(function(){
console.log(interval);
if(interval == 1000){ /*interval you dont want anymore or increment/decrement */
interval = 3600000; /* Increment you do want for timer */
clearInterval(timer);
timing();
}
},interval);
}
timing();
Alternately if you wanted to just have something happen at start and then forever at a specific interval you could just call it at the same time as the setInterval. For example:
var this = function(){
//do
}
setInterval(function(){
this()
},3600000)
this()
Here we have this run the first time and then every hour.
I couldn't synchronize and change the speed my setIntervals too and I was about to post a question. But I think I've found a way. It should certainly be improved because I'm a beginner. So, I'd gladly read your comments/remarks about this.
<body onload="foo()">
<div id="count1">0</div>
<div id="count2">2nd counter is stopped</div>
<button onclick="speed0()">pause</button>
<button onclick="speedx(1)">normal speed</button>
<button onclick="speedx(2)">speed x2</button>
<button onclick="speedx(4)">speed x4</button>
<button onclick="startTimer2()">Start second timer</button>
</body>
<script>
var count1 = 0,
count2 = 0,
greenlight = new Boolean(0), //blocks 2nd counter
speed = 1000, //1second
countingSpeed;
function foo(){
countingSpeed = setInterval(function(){
counter1();
counter2();
},speed);
}
function counter1(){
count1++;
document.getElementById("count1").innerHTML=count1;
}
function counter2(){
if (greenlight != false) {
count2++;
document.getElementById("count2").innerHTML=count2;
}
}
function startTimer2(){
//while the button hasn't been clicked, greenlight boolean is false
//thus, the 2nd timer is blocked
greenlight = true;
counter2();
//counter2() is greenlighted
}
//these functions modify the speed of the counters
function speed0(){
clearInterval(countingSpeed);
}
function speedx(a){
clearInterval(countingSpeed);
speed=1000/a;
foo();
}
</script>
If you want the counters to begin to increase once the page is loaded, put counter1() and counter2() in foo() before countingSpeed is called. Otherwise, it takes speed milliseconds before execution.
EDIT : Shorter answer.
(function variableInterval() {
//whatever needs to be done
interval *= 2; //deal with your interval
setTimeout(variableInterval, interval);
//whatever needs to be done
})();
can't get any shorter
Here is yet another way to create a decelerating/accelerating interval timer. The interval gets multiplied by a factor until a total time is exceeded.
function setChangingInterval(callback, startInterval, factor, totalTime) {
let remainingTime = totalTime;
let interval = startInterval;
const internalTimer = () => {
remainingTime -= interval ;
interval *= factor;
if (remainingTime >= 0) {
setTimeout(internalTimer, interval);
callback();
}
};
internalTimer();
}
Make new function:
// set Time interval
$("3000,18000").Multitimeout();
jQuery.fn.extend({
Multitimeout: function () {
var res = this.selector.split(",");
$.each(res, function (index, val) { setTimeout(function () {
//...Call function
temp();
}, val); });
return true;
}
});
function temp()
{
alert();
}
This piece of code below accelerates (acceleration > 1) or decelerates (acceleration <1) a setInterval function :
function accelerate(yourfunction, timer, refresh, acceleration) {
var new_timer = timer / acceleration;
var refresh_init = refresh;//save this user defined value
if (refresh < new_timer ){//avoid reseting the interval before it has produced anything.
refresh = new_timer + 1 ;
};
var lastInter = setInterval(yourfunction, new_timer);
console.log("timer:", new_timer);
function stopLastInter() {
clearInterval(lastInter);
accelerate(yourfunction, new_timer, refresh_init, acceleration);
console.log("refresh:", refresh);
};
setTimeout(stopLastInter, refresh);
}
With :
timer: the setInterval initial value in ms (increasing or decreasing)
refresh: the time before a new value of timer is calculated. This is the step lenght
acceleration: the gap between the old and the next timer value. This is the step height
Inspired by the internal callback above, i made a function to fire a callback at fractions of minutes. If timeout is set to intervals like 6 000, 15 000, 30 000, 60 000 it will continuously adapt the intervals in sync to the exact transition to the next minute of your system clock.
//Interval timer to trigger on even minute intervals
function setIntervalSynced(callback, intervalMs) {
//Calculate time to next modulus timer event
var betterInterval = function () {
var d = new Date();
var millis = (d.getMinutes() * 60 + d.getSeconds()) * 1000 + d.getMilliseconds();
return intervalMs - millis % intervalMs;
};
//Internal callback
var internalCallback = function () {
return function () {
setTimeout(internalCallback, betterInterval());
callback();
}
}();
//Initial call to start internal callback
setTimeout(internalCallback, betterInterval());
};
This is my idea for times when you do not want loops like setInterval to overlap.
You also want to be able to set the loop execution delay and start and stop the loop, instansly on the fly.
I am using a loop_flag variable and a setTimeout function.
I set the main function to async so that you can call other functions in the body by calling await. When the main body of your code is running, the main loop waits and does not repeat itself. (which is not the case with setInterval)
An example of a simple code is:
//#NabiKAZ
document.getElementById("btn_start").addEventListener("click", function() {
console.log("Starting...");
loop_flag = true;
loop_func();
});
document.getElementById("btn_stop").addEventListener("click", function() {
console.log("Stoping...");
loop_flag = false;
});
var n = 0;
var loop_flag = false;
var loop_func = async function() {
if (!loop_flag) {
console.log("STOP.");
return;
}
//body main function inhere
n++;
console.log(n);
////
if (loop_flag) {
setTimeout(loop_func, document.getElementById("inp_delay").value);
} else {
console.log("STOP.");
}
}
<input id="inp_delay" value="1000">
<button id="btn_start">START</button>
<button id="btn_stop">STOP</button>
For a more complete code with a fetch request inside the loop, see here:
https://jsfiddle.net/NabiKAZ/a5hdw2bo/
You can use a variable and change the variable instead.
setInterval(() => function, variable)
You can do this by clearing the interval every iteration, changing the timer value and setting the interval again. Hope it helps ;)
For exemple:
const DOMCounter = document.querySelector(".counter")
let timer = 1000
const changeCounter = () => {
clearInterval(interval)
DOMCounter.innerHTML = timer
timer += 1000
timer == 5000 && timer == 1000
interval = setInterval(changeCounter, timer)
}
let interval = setInterval(changeCounter, timer)
<div class="container">
<p class="counter"></p>
</div>
var counter = 15;
var interval = function() {
setTimeout(function(){
// Write your code here and remove console.log, remember that you need declare yourDynamicValue and give it a value
console.log((new Date()).getTime())
window.counter = yourDynamicValue;
window.interval();
}, counter);
}
// It needs to run just once as init
interval();

Categories

Resources