can´t manage timeouts in for loop in javascript - javascript

I'm trying to do some kind of Simon game in javascript. I have already made a table, and a function that changes any cell by its id to another id, so it's color changes for 300 miliseconds. This is the code:
var seleccionarcelda = function(object){
var id=object.id;
object.id="selected";
setTimeout(function(){object.id=id;},300);
}
Then, for a sequence as large as I want it to, it should light a random cell, and then another cell, in a for loop
var secuencia = function(numero){
for(j=0; j<numero; j++){
var cel="t"+ Math.floor((Math.random() * 6) + 1);
console.log(cel);
seleccionarcelda(document.getElementById(cel));
}
}
The problem is that it kind of works, but selects all cells at once, and not in order as it should do. How can I fix it?

You could pass the loop index into the function and use that as a multiplier of the delay time.
Something like:
var seleccionarcelda = function(object, index){
// increment delay timer
var delay = (index + 1) * 300;
var id=object.id;
object.id="selected";
setTimeout(function(){object.id=id;}, delay );// use variable for timer delay
}
var secuencia = function(numero){
for(j=0; j<numero; j++){
var cel="t"+ Math.floor((Math.random() * 6) + 1);
console.log(cel);
// pass j to function
seleccionarcelda(document.getElementById(cel), j);
}
}
What is happening now is the loop completes in milliseconds and therefore all the setTimeout start at basically the same time

Related

How do I delay this code running in JavaScript?

I have written this code to change an image:
change = function(){
for (r=0; r<6; r++){
for (i = 0; i < 6 ; i++) {
setInterval(imgfile(number=i+1), 5000);
}
}
}
imgfile= function(number){
a = 'document.getElementById("imgdiv").src = "images/'+number+'.svg"';
eval(a);
}
The function change() is called when a button is clicked.
When I press the button the image changes straight to 6.svg, when I want it to go through the images 1, 2, 3, 4, 5, 6 and to repeat it 6 times. When I change setInterval to change.setInterval or imgfile.setInterval it doesn't work at all. How do I fix this?
change = function(i=0){
imgfile(i%6+1);//change image
if(i<36) setTimeout(change,5000,i+1);//next image in 5 seconds
}
imgfile= function(number){
document.getElementById("imgdiv").src = "images/"+number+".svg";//no need to use ev(i||a)l
}
Instead of loop/interval mess you can simply start a timeout that restarts itself after changing the image... This code will loop over 6 images with a delay of 5 seconds and that 6 times...
Something like this, perhaps?
var index, imgCount, loopCount, imgTag, countdown;
index = 0;
imgCount = 6;
loopCount = 6;
imgTag = document.getElementById('imgdiv');
countdown = function () {
if (index < imgCount * loopCount) {
imgTag.src = 'images/' + index % imgCount + '.svg';
index = index + 1;
setTimeout(countdown, 5000);
}
};
countdown();
Here we're avoiding the double loop and using modular math (index % imgCount) to get the right file number.
For another question I wrote a nice utility function that has quite a number of uses, but can also handle this scenario very easily. The main issue is that there is no time elapsing between the different delays being set. So you are setting 6 different actions to all happen within 5000ms, and all will occur at the same moment.
Here's my original answer
Here's the utility function for that answer, along with its application to your problem.
function doHeavyTask(params) {
var totalMillisAllotted = params.totalMillisAllotted;
var totalTasks = params.totalTasks;
var tasksPerTick = params.tasksPerTick;
var tasksCompleted = 0;
var totalTicks = Math.ceil(totalTasks / tasksPerTick);
var initialDelay = params.initialDelay;
var interval = null;
if (totalTicks === 0) return;
var doTick = function() {
var totalByEndOfTick = Math.min(tasksCompleted + tasksPerTick, totalTasks);
do {
params.task(tasksCompleted++);
} while(tasksCompleted < totalByEndOfTick);
if (tasksCompleted >= totalTasks) clearInterval(interval);
};
// Tick once immediately, and then as many times as needed using setInterval
if (!initialDelay) doTick();
if (tasksCompleted < totalTicks) interval = setInterval(doTick, totalMillisAllotted / totalTicks);
}
// Do 6 actions over the course of 5000 x 6 milliseconds
doHeavyTask({
totalMillisAllotted: 5000 * 6,
totalTasks: 6,
tasksPerTick: 1,
initialDelay: false, // Controls if the 1st tick should occur immediately
task: function(n) { console.log('Set image to "images/' + (n + 1) + '.svg"'); }
});
You want to do setTimeout().
setTimeout pauses for the millesecond value and then does the code. Where setInterval runs the code every whatever milleseconds.
Yeah, don't do change.setInterval or whatever, it is just setInterval.
An example for you would be this inside the for loop to replace the setInterval function.
setTimeout(imgfile(i+1), 5000);

function to calculate function fps

Ok so I believe I could best describe the issue through code so here goes
var clicks = 0;
//function to calculate
function clicking(){
clicks += 1;
}
//function to calculate fps where fn is the name of the function
function FPS(fn){
//do stuff
}
Okay so to clarify I dont want to add a variable to the actual function clicking I would like to be able to call something like
FPS(clicking) and have the function return a value for example
var fps = FPS(clicking);
then i could display the returned number as such
element.innerHTML = fps
EDIT:
I know with the current code it seems silly but this is just example coding not what I am actually using
This is not very actual since Date.now() also uses time.
function FPS(fn) {
var startTime = Date.now();
fn();
var endTime = Date.now();
return endTime - startTime;
}
function longClick() {
var abc = 0;
for (var i = 0; i < 100000000; i++) {
abc++;
}
}
var fps = FPS(longClick);
console.log((fps / 1000) + ' seconds');
FPS usually refers to Frames Per Second which is the frequency of refreshing screen image.
Pick a more comprehensive name, with keywords like Elapsed, for teammates.
If you want to know "how fast the functions runs" :
/**
* Return the execution time of a function
* #param {Function} fn The function to execute
*/
function FPS(fn) {
var start = new Date().getTime();
fn();
var end = new Date().getTime();
return end - start;
}
You can have FPS return a DOMHighResTimeStamp (works in IE 10+, Firefox 15+, Chrome 20+, Safari 8+) which will return time in milliseconds. If you want it to work in older browser you can replace the precision timing with a Date (new Date()) object but the Date object will only nab you time in seconds (not milliseconds):
var clicks = 0;
//function to calculate
function clicking(){
clicks += 1;
}
//function to calculate fps where fn is the name of the function
function FPS(fn){
var start = performance.now();
fn();
return performance.now() - start;
}
console.log("Function took " + FPS(clicking) + " milliseconds!");
Here is some pseudo code to get what I think you're looking for - assuming you have a game loop and you're calling FPS in your game loop. As I said - more details about the specifics (such as components) of your game would be helpful.
var clicks = 0;
var fps = 0;
var elapsedTime;
//function to calculate
function clicking(){
clicks += 1;
}
//function to calculate fps where fn is the name of the function
function FPS(fn){
// Get start time
//call fn
// Get stop time
// var delta = stop time - start time
// elapsedTime += delta;
// fps++;
// If elapsedTime > 1 second
// then while elapsedTime > 1 second... elapsedTime -= 1 second and fps = 0;
// We use the while loop here in the event that it took more than 1 second in the call
// But you could just reset elapsedTime back to 0
}
This FPS(fn) would be called anywhere in your game in place of the original function to see how many times that function is called a second.
To answer the original question:
Okay so to clarify I dont want to add a variable to the actual function clicking I would like to be able to call something like FPS(clicking) and have the function return a value for example
Firstly you need to return a value from your clicking function, or any function you plan to pass to the FPS method.
var clicks = 0;
//function to calculate
function clicking(){
clicks += 1;
return clicks;
}
Then in your FPS function, you need to return that value as well.
//function to calculate fps where fn is the name of the function
function FPS(fn){
//do stuff
return fn();
}

For loop inside if statement will not run and I'm not sure why

A brief run-down - I'm using the wad JavaScript library to create a random tone generator. I am trying to have the generator 'slide' between pitches, as follows in pseudocode:
Determine the current pitch (randomNum)
Determine the next pitch (randomNext)
Determine whether the next pitch is higher or lower
Play the current pitch and hold it for a second
Play all the pitches between the current pitch and the next pitch quickly to give a 'sliding' effect - this is the part that isn't working!
Play the next pitch and hold it for a second (and then reassign it as the current pitch).
I have got everything working except for step 5. I am trying to use a for loop (within an if/else statement according to whether the next pitch is higher or lower) to iterate over each pitch using a counter, but according to my logged console statements, the for loop never runs at all.
If I remove the for loop from the if statement and set its starter pitch manually from a variable, it runs as expected.
What am I doing wrong? Is there something wrong with my if statement? With my for loop?
The problematic bit of code is this:
// play the first pitch
function playFirst() {
if(!stopped){
window.randomNum = Math.round(Math.random()*200 + 100);
console.log("randomnum is now "+ randomNum);
}
playRandom();
}
// play and loop subsequent pitches
function playRandom(){
if(!stopped){
var randomNext = Math.round(Math.random()*200 + 100);
console.log("randomNext is now " + randomNext);
var howManyCents = randomNum - randomNext;
console.log(howManyCents + " cents");
// ascending note slide condition
if (randomNum < randomNext) {
console.log("randomnum is less!");
var inbetweenNum = randomNum + 1;
// for loop - the part with the problem!
for (var i = 0; i < howManyCents; i++) {
inbetweenNum = randomNum + i;
console.log("inbetween number is " + inbetweenNum);
inbetween.play({ pitch : inbetweenNum });
console.log("played inbetween up");
}
// descending note slide condition
} else {
console.log("randomnum is more!");
var inbetweenNum = randomNum - 1;
// another problematic for loop
for (var i = 0; i > howManyCents; i--) {
inbetweenNum = randomNum - i;
console.log("inbetween number is " + inbetweenNum);
inbetween.play({ pitch : inbetweenNum });
console.log("played inbetween down");
}
}
// actually play the note
bell.play({ pitch : randomNext, wait: 0 });
console.log("played randomnext" + randomNext);
// reassign the new note as the current note
randomNum = randomNext;
console.log("randomnum is now" + randomNum);
setTimeout(playRandom,1500); // and loop it
}
}
and I have made a JSFiddle of the full program here.
Any assistance would be very much appreciated!
The condition for that block is that randomNum < randomNext, but howManyCents is randomNum - randomNext, which will be negative in that case. The loop condition, then – i < howManyCents, with i starting at 0, will never be true.
You can use i < -howManyCents, or assign Math.abs(randomNum - randomNext) to howManyCents, or i > howManyCents and i--.

For Loop Delay Without setTimeout

So, here's my problem... I've got a for loop inside a function
var fan1 = function () {
for(var i=0; i<flare1base.length; i++) {
for(var i=0; i<200; i++)
flare1base.rotation.z += 0.01;
};
};
It should do essentially that, fairly simple, but... I need it to wait 10 milliseconds before adding to the rotation again. The problem is I don't think I can use setTimeout or just use setInterval instead of the entire for loop, because it's acting on an object in an array, and if I do
f1 = setInterval("flare1array[i].rotation.z += 0.01",10);
setTimeout("clearInterval(f1)",2000);
It queues up an action to do rotate the thingy, but by the time the action occurs the for loop has gone around again and "i" is different.
See jQuery: Wait/Delay 1 second without executing code
Have you tried:
setTimeout(function (){
flare1array[i].rotation.z += 0.01;
}, 10);
How about using setInterval and then clearInterval? You could do something like this:
function rotate(max) {
var i = 0;
return function () {
flare1base.rotation.z += 0.01;
if (++i == max) clearInterval(id);
}
}
var id = setInterval(rotate(200), 10);
Similar example on JSFiddle
setInterval returns an id that can later be passed to clearInterval to prevent the action from continuing indefinitely. Here I've wrapped a counter variable i in a closure, which keeps track of how many times the inner function has been called. When it has been called max times, it stops.
By the way, if you know what the starting value of flare1base.rotation.z is, it would be better to calculate it afresh each time the function is called rather than continuously adding 0.01, as the result of repeated floating point additions may be imprecise. For example, if you know it starts at, you could do flare1base.rotation.z = 0.01 * ++i; (and remove the increment from the if statement).
To extend this to an array of items, you can wrap the whole process in a loop. Assuming that your items are in an array arr:
function rotate(arr, idx, max) {
var i = 0;
return function () {
arr[idx] += 0.01;
if (++i == max) clearInterval(ids[idx]);
}
}
var ids = new Array(5);
for (var i = 0; i < 5; ++i) {
ids[i] = setInterval(rotate(arr, i, 200), 10);
}
updated JSFiddle

Javascript setTimeout not working in a for-loop

I have to change the source of an image every second. I have a for loop in which a call a function that has a timeout. I read that here, on stackOverflow, but it doesn't work. Can please someone tell me what can I fix to make it work? I've been struggling with this for much more that I'd like to admit. Thanks.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script type="text/javascript">
function changeImage(k) {
setTimeout(function(){
document.getElementById("img").src = k + ".png"; alert(k );}, 1000);
}
function test() {
for (var k = 1; k <= 3; k++) {
changeImage(k);
}
}
</script>
</head>
<body>
<div id="main_img">
<img id="img" src="http://placehold.it/110x110">
</div>
<input type="button" style="width: 200px" onclick="test()" />
</body>
In your code, you set all the timeouts at once. So if you set them all one second from now they all fire one second from now.
You are already passing in the index k so just multiply the time parameter by k.
setTimeout(function(){
document.getElementById("img").src = k + ".png";
alert(k);
}, k * 1000);
// ^ added
The problem is that you are creating instances of a timer milliseconds apart. One second later, they all execute milliseconds apart as well. What you need is to execute them at a set interval apart from each other.
You can use a timer using setInterval, which executes the provided function at a given interval. Don't forget to kill-off the timer though, otherwise it will run forever.
Minor optimizations
You can cache the element in a variable so you won't be hitting the DOM that frequently.
Also, I'd avoid the alert(). If you are debugging, use breakpoints in the debugger. If you really want it to be "alert-like", then use console.log and watch the console.
An advantage of setInterval over a recursive setTimeout is that you will not be spawning multiple timers per iteration, but instead, just one timer.
And here's the proposed solution:
var k = 0;
var image = document.getElementById("img");
var interval = setInterval(function() {
// Increment or clear when finished. Otherwise, you'll leave the timer running.
if(k++ < 3) clearInterval(interval);
image.src = k + ".png";
// Execute block every 1000ms (1 second)
},1000);
Instead of using loop, you can do it like this:
var k = 0;
var int = setInterval(function() {
if (k <= 3) k++;
else { clearInterval(int); }
document.getElementById("img").src = k + ".png";
alert(k);
}, 1000);
My advice is to use console.log() or alert() to help you debug - it'll make it a LOT more obvious what's going on. For instance, if you put a console.log in your test or setTimeout functions, you'd see that all three images were getting added at the same time.
What I'd recommend is to declare your "nextImage" function, then define your setTimeout within that function. That way it'll call itself every second.
Another tip: I assume you want the three images to loop forever, so I added an often used trick with the modulus operator (%) to accomplish this.
Have a look:
Working demo: http://jsfiddle.net/franksvalli/PL63J/2/
(function(){
var numImages = 3, // total count of images
curImage = 1, // start with image 1
$image = document.getElementById("img"),
imageBase = "http://placehold.it/110x11";
function nextImage() {
$image.src = imageBase + curImage;
// increment by one, but loop back to 1 if the count exceeds numImages
curImage = (curImage % numImages) + 1;
// execute nextImage again in roughly 1 second
window.setTimeout(nextImage, 1000);
}
// initializer. Hook this into a click event if you need to
nextImage();
})();
As other folks have said, you probably want to use setInterval, which you can do with some tweaks:
(function(){
var numImages = 3, // total count of images
curImage = 1, // start with image 1
$image = document.getElementById("img"),
imageBase = "http://placehold.it/110x11";
function nextImage() {
$image.src = imageBase + curImage;
// increment by one, but loop back to 1 if the count exceeds numImages
curImage = (curImage % numImages) + 1;
}
// initializer. Hook this into a click event if you need to
nextImage(); // call function immediately without delay
window.setInterval(nextImage, 1000);
})();
The problem
setTimeout doesn't stop the program execution but only sets up an event for a callback in 1 second. What that means is that if you setup three setTimeout's inside your for loop, they will execute simultaneously after 1 second.
A solution
Instead of using a for loop, you can use a delayed recursion.
function changeImage(imageIndex) {
document.getElementById("img").src = imageIndex + ".png";
alert(imageIndex);
}
function myLoop( imageIndex ) {
if( imageIndex >= 3 ) return;
changeImage( imageIndex );
setTimeut( function() { myLoop(imageIndex + 1) }, 1000 );
}
setTimeut( function() { myLoop(0) }, 1000 );
Another solution using setInterval
var interval = null;
var imageIndex = 0;
function changeImage() {
document.getElementById("img").src = imageIndex + ".png";
alert(imageIndex);
imageIndex++;
if( imageIndex === 3 ) clearInterval( interval );
}
interval = setInterval( changeImage , 1000);
Using different delays
function changeImage(imageIndex) {
document.getElementById("img").src = imageIndex + ".png";
alert(imageIndex);
}
for( var i=0; i < 3; i++) {
setTimeout( changeImage.bind(window, i), i * 1000 );
}
A groovy one liner( please don't use this, ever! )
(function f(i) { setTimeout( changeImage(i) || f.bind(window, i = (i++)%3), 1000); })(0)
WHY IT DOESN'T WORK?
Because Javascript always passes variables by reference. When your code is waiting on the queue, the variables have already changed.
MY SOLUTION:
Create an array and push whatever codes you want to execute in order of appearance (Place the real value of the variables directly) e.g.:
var launcher = [];
launcher.push('alert("First line of code with variable '+ x +'")');
launcher.push('alert("Second line of code with variable '+ y +'")');
launcher.push('alert("Third line of code with variable '+ z +'")');
Use setInterval instead of setTimeout to execute the codes (You can even change the delay period dynamically) e.g.
var loop = launcher.length;
var i = 0;
var i1 = setInterval(function(){
eval(launcher[count]);
count++;
if(i >= loop) {
clearInterval(i1);
}
}, 20);

Categories

Resources