How can I make the clearInterval stop without creating the loop? - javascript

So, I'm very, very new to Javascript, but I'm going through a problem with setInterval and clearInterval. The problem itself is to generate a number between 0 and 1. I'm supposed to console.log the answer if it's over .75, and log how many times it takes to get a number that's over .75. I figured out the Math portion, but my issue is that even though I have the setInterval correct (I think), the clearInterval isn't actually stopping it, but it keeps going, almost like an infinite loop. What am I doing wrong here?
let counter = 0
// var interval = setInterval(randomGame, 1000)
function randomGame(){
setInterval(function () {
// console.log(Math.random())
if (Math.random() > .75){
clearInterval()
}else {
console.log(counter++)
}
}, 1000)
}

You need to save a reference to the interval and then stop it using this reference, constant for example.
let counter = 0
function randomGame(){
const interval = setInterval(function () {
if (Math.random() > .75){
clearInterval(interval);
}else {
console.log(counter++)
}
}, 1000)
}
randomGame();

Related

Javascript simple game loop - how to interval?

I have a simple game loop that goes like this:
function update(progress){
//do something each second
//do something else twice per second
//do something else each two seconds
}
function draw() {
//code here...
}
function gameLoop(timestamp) {
update(progress)
draw()
var progress = (timestamp - lastRender)
lastRender = timestamp
window.requestAnimationFrame(gameLoop)
}
var lastRender = 0
window.requestAnimationFrame(gameLoop)
How can I make sure to execute some actions into the update function each helf second, second, or two seconds?
Thank you
If you want to define the interval, you'll need to use setInterval. requestAnimationFrame will only update based on the refresh rate of the screen so you cannot define your own interval with this. setInterval has lots of downsides though, so it is recommended to sync up the interval with the refresh rate using something like this:
let doUpdate = false
setInterval(() => doUpdate = true), 1000)
const render = () => {
if (doUpdate) {
// your code
doUpdate = false
}
}
window.requestAnimationFrame(render)
If you want to perform an action periodically when using game loop based on delta time, the basic idea is for you to keep a counter of the elapsed time. For each iteration, you then add the time difference until it reaches the intended period.
Applied to your code, it would look something like this:
let oneSecondCounter = 0
let twoSecondCounter = 0
function update(progress){
oneSecondCounter += progress
if (oneSecondCounter >= 1000) {
// Code here will be executed every 1000ms
oneSecondCounter = 0
}
twoSecondCounter += progress
if (twoSecondCounter >= 2000) {
// Code here will be executed every 2000ms
twoSecondCounter = 0
}
}
function draw() {}
function gameLoop(timestamp) {
var progress = (timestamp - lastRender)
update(progress)
draw()
lastRender = timestamp
window.requestAnimationFrame(gameLoop)
}
var lastRender = performance.now()
window.requestAnimationFrame(gameLoop)
However, this means you have to make a counter variable for every periodic action you want to perform. Instead of creating a separate variable, we can bundle the counter together with the function using a closure. The closure lets each function to have their own independent counter.
The closure that we are going to use looks like this:
function initPeriodicFunction(fn, runEvery) {
let counter = 0
return function (deltaTime) {
counter += deltaTime
if (counter >= runEvery) {
fn()
counter = 0
}
}
}
Now, instead of having to create a counter variable for each action, we can just pass a function to the initPeriodicFunction and get a new function which will run only periodically.
// You can use an arrow function
const runEverySecond = initPeriodicFunction(() => console.log('One second'), 1000)
// Or you can pass a function
function runThis() {
console.log('Two seconds')
}
const runEveryTwoSeconds = initPeriodicFunction(runThis, 2000)
function update(progress){
runEverySecond(progress)
runEveryTwoSeconds(progress)
}
Depending on your use case, the method above might be enough. If you're going to perform a more accurate tasks (e.g. physics engine), it would be better to separate the frame rate from the update rate. This is similar to how Unity's FixedUpdate works.
Imagine you want to perform a physics update every 100ms. If somehow the update call was delayed, for example 600ms after the last update, instead of performing a single update, we perform 6 updates, each with 100ms chunk. This results in more precise step-by-step calculation.
To perform this kind of fixed update, the initialization function need to be modified as follows:
function initPeriodicFunction(fn, runEvery) {
let counter = 0
return function (deltaTime) {
counter += deltaTime
while (counter >= runEvery) {
fn()
counter -= runEvery
}
}
}
Now, the function will be run either once or multiple times depending on how long has elapsed since the last update call.
setInterval(function {
//code that happends every second goes here
}, 1000);
use setInterval().
This creates a timer and will call the function every x seconds.

interval keeps firing even though clearInterval has been called

I am trying to get a function to run 10 times with a pause inbetween each run, yet when I try to it repeats the function infinite times then after 10 times it pauses, and so on. Right now this is the code with the problem:
for(i=0;i<10;i++) {
console.log(i);
interval = setInterval(function() {console.log("Function ran");}, 1000);
}
window.clearInterval(interval);
Console:0123456789Function ran["Function ran" is repeated infinite times after "9"]
interval = setInterval(function() {console.log("Function ran");}, 1000);
This line creates a new interval-instance each time, which means you have created 10 intervals. At the end of the loop interval holds the id of the last interval that was created. Hence that's the only one you're clearing, and the other ones are still running.
To cancel the interval, you need to keep track of how many times the function has been invoked. One way you can do that is as follows:
function pauseAndRepeat(delay, iterations, func) {
var i = 0;
var interval = setInterval(function() {
func();
if(++i === iterations) {
clearInterval(interval);
}
}, delay);
}
Here we have a function that defines a counter (i) in its local scope. Then it creates an interval using a function that checks the counter to see if it should call your function (func) or clear the interval when it is done. interval will have been set when the interval-handler is actually called. In this case the handler is basically a closure since it is bound to the local scope of pauseAndRepeat.
Then you can invoke the function as follows:
pauseAndRepeat(1000, 10, function() {
console.log("Function ran");
});
This will print out Function ran ten times, pausing for a second each time.
setInterval is expected to run forever, on an interval. Every time you call setInterval here, you have a new infinite loop running your function every 10s, and as others have noted you only are canceling the last one.
You may do better with chained setTimeout calls:
var counter = 0;
function next() {
if (counter < 10) {
counter++;
setTimeout(function() {
console.log("Function ran");
next();
}, 1000);
}
}
next();
This chains delayed functions, setting a timeout for the next one after each runs. You can do something similar with setInterval and cancellation:
var counter = 0;
var intervalId = setInterval(function() {
console.log("Function ran");
if (++counter >= 10) {
clearInterval(intervalId);
}
}, 1000);
In both these cases the key issue is that you trigger the next run or cancel the interval within the callback function, not in synchronous code.

Define a timer in JavaScript

I have a function in JavaScript. I use setInterval in order to control my function. I also have another logic for controlling my function. I have a counter which is increased once one condition happens and is decreased when another condition happens. Now, sometimes the second condition does not happen and hence my function won't be resume anymore. (I pause my function when my first condition happen). Therefore, I want to wait at most 30 seconds for the second condition. If it does not happen, then I want to resume my function anyway. I have following code, but it does not work as I expect it. What happens is that it resume my function every 30 seconds. Then, it may be resumed while it should wait. Can someone let me know what is the problem with my code?
Please note that, the value for the counter may increase to more than 20. I mean the first and second condition may occur more than once.
function main()
{
// body
}
function increaseCounter()
{
counter += 1;
clearInterval(controller);
controlSecond = setInterval(function(){
counterSeconds += 1;
if (counterSeconds == 30)
{
counterSeconds = 0;
controller = setInterval(main, 100);
clearInterval(controlSecond);
}
}, 1000);
}
function decreaseCounter()
{
counter -= 1;
if (counter == 0)
{
counterSeconds = 0;
clearInterval(controlSecond);
controller = setInterval(main, 100);
}
}
Consider what happens if you call increaseCounter twice in a row.
On the first execution it will create interval A and assign it to controlSecond.
On the second execution it will create interval B and assign it to controlSecond, while interval A continues to fire off indefinitely. You won't stop it with clearInterval(controlSecond) because controlSecond no longer references interval A.
The problem is that you continue to set controlSecond and controller to a new interval without clearing them first. That results in the intervals being leaked with no way of clearing them. It's sort of like a memory leak where you have dynamically allocated memory but nothing pointed at it, but instead of renegade memory you have renegade intervals.
One way to prevent this is to make sure you always clear your interval before setting it.
I would also recommend that you implement controlSecond with a setTimeout because that is designed for tasks which only happen once.
Why not
var counter = 0
var timeout = null
function main () {
clearTimeout(timeout);
timeout = null;
}
function increaseCounter () {
counter++;
if (!timeout)
timeout = setTimeout(main, 30*1000);
}
function decreaseCounter() {
counter--;
if (counter === 0)
main();
}

Javascript setTimeout in foreach: need help creating a closure

I have this function
notes.forEach(function(note) {
setTimeout(function() {
playNote(note);
}, 1000);
});
This doesn't work. It plays all the notes at the same time, instead of playing them sequentially with a 1 second gap in between. It looks like I need to have a closure here to make this work. Could someone help me fix this function so it would play the note with the delay between each note?
There are two ways to do this:
1) Have a function that grabs one note every second until there are no more notes:
var interval = setInterval(function() {
playNote(notes.shift()); // changes the notes array!
if (!notes.length) clearInterval(interval);
}, 1000);
2) Start all the timers at the same time with different delays:
notes.forEach(function(note, index) {
setTimeout(playNote.bind(null, note), index*1000);
});
because all timeouts are set at the same time...
Do something like this:
playAllNotes(0);
function playAllNotes(index) {
if(notes.length > index) {
setTimeout(function() {
playNote(notes[index]);
playAllNotes(++index);
}, 1000);
}
}
You can use a counter, it's tricky but worth it if you are working with objects:
counter = 0;
$.each(object, function(index,item){
counter++;
var localCounter = counter;
setTimeout(function{
console.log('something')
}, counter * 1000) // change one to number of milliseconds you need
})
First counter is global, so if we don't user var localCounter we would execute all timeouts at the same time.

Timing in JS - multiple setIntervals running at once and starting at the same time?

Let's say I have a function:
myFunc = function(number) {
console.log("Booyah! "+number);
}
And I want it to run on a set interval. Sounds like I should use setInterval, huh!
But what if I want to run multiple intervals of the same function, all starting at the exact same time?
setInterval(function(){
myFunc(1);
}, 500);
setInterval(function(){
myFunc(2);
}, 1000);
setInterval(function(){
myFunc(3);
}, 2000);
So that the first runs exactly twice in the time it takes the second to run once, and the same between the second and third.
How do you make sure that they all start at the same time so that they are in sync?
Good question, but in JS you can't. To have multiple functions in the same program execute at the same time you need multi-threading and some deep timing and thread handling skills. JS is single threaded. setInterval doesn't acutally run the function after the delay, rather after the delay it adds the function to the event stack to be run as soon as the processor can get to it. If the proc is busy with another operation, it will take longer than the delay period to actually run. Multiple intervals/timeouts are all adding calls to the same event stack, so they run in turn as the proc is available.
function Timer(funct, delayMs, times)
{
if(times==undefined)
{
times=-1;
}
if(delayMs==undefined)
{
delayMs=10;
}
this.funct=funct;
var times=times;
var timesCount=0;
var ticks = (delayMs/10)|0;
var count=0;
Timer.instances.push(this);
this.tick = function()
{
if(count>=ticks)
{
this.funct();
count=0;
if(times>-1)
{
timesCount++;
if(timesCount>=times)
{
this.stop();
}
}
}
count++;
};
this.stop=function()
{
var index = Timer.instances.indexOf(this);
Timer.instances.splice(index, 1);
};
}
Timer.instances=[];
Timer.ontick=function()
{
for(var i in Timer.instances)
{
Timer.instances[i].tick();
}
};
window.setInterval(Timer.ontick, 10);
And to use it:
function onTick()
{
window.alert('test');
}
function onTick2()
{
window.alert('test2');
}
var timer = new Timer(onTick, 2000,-1);
var timer = new Timer(onTick2, 16000,-1);
For a finite number of ticks, change the last parameter to a positive integer for number. I used -1 to indicate continuous running.
Ignore anyone who tells you that you can't. You can make it do just about any thing you like!
You can make something like this.
arr = Array();
arr[0] = "hi";
arr[1] = "bye";
setTimer0 = setInterval(function(id){
console.log(arr[id])
},1000,(0));
setTimer1 = setInterval(function(id){
console.log(arr[id]);
},500,(1));
Hope it helps!
JavaScript is single threaded. You can use html5 web worker or try using setTimeout recursively. Create multiple functions following this example:
var interval = setTimeout(appendDateToBody, 5000);
function appendDateToBody() {
document.body.appendChild(
document.createTextNode(new Date() + " "));
interval = setTimeout(appendDateToBody, 5000);
}
Read this article:
http://weblogs.asp.net/bleroy/archive/2009/05/14/setinterval-is-moderately-evil.aspx
You can use multiples of ticks inside functions, in the example below you can run one function every 0.1 sec, and another every 1 sec.
Obviously, the timing will go wrong if functions require longer times than the intervals you set. You might need to experiment with the values to make them work or tolerate the incorrect timing.
Set a variable to handle tick multiples
let tickDivider = -1
Increase the value of tick variable inside the faster function
const fastFunc = ()=> {
tickDivider += 1
console.log('fastFunciton')
}
Use a condition to on running the slower function
const slowFunc = ()=> {
if (!(tickDivider % 10)){
console.log('slowFunction')
}
}
Call both functions in a single one. The order is not important unless you set tickDivider to 0 (of any multiple of 10)
const updateAllFuncs = () => {
fastFunc()
slowFunc()
}
Set the interval to the frequency of the faster function
setInterval(updateAllFuncs, 100)
What I'm doing here is adding a speed attribute to the HTML elements. These speeds are passed as a parameter to setCounter(). I did this mainly to make the code easier to test and play with.
The function setCounter() is invoked inside a loop for every HTML element with class counter. This function sets a new setInterval in every execution.
The intervals seem to be working in sync.
const elements = document.querySelectorAll('.counter')
elements.forEach((el, i) => {
let speed = Number(elements[i].getAttribute('speed'))
setCounter(el, speed, 5000)
})
function setCounter(element, speed, elapse){
let count = 0
setInterval(() => {
count = (count >= elapse) ? elapse : count + speed
if(count === elapse) clearInterval()
element.innerHTML = count
}, 1)
}
Same Speeds
<p class="counter" speed='10'></p>
<p class="counter" speed='10'></p>
Different Speeds
<p class="counter" speed='3'></p>
<p class="counter" speed='5'></p>

Categories

Resources