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
Related
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
I want to slow down console.log in my loop
// function update to actualize value
function update() {
requestAnimationFrame(update);
analyser.getByteFrequencyData(data);
var count=0;
for (var i=data.length; i--;) {
count+=data[i];
if(count >= 1) {
console.log(data);
}
};
}
For example, show one console.log immediatly, and then, each .5s
How can we do that ?
(maybe with setTimeout() but I don't want start delay)
The simplest way would be to introduce a timeout before running the update again each time...
// function update to actualize value
function update() {
analyser.getByteFrequencyData(data);
var count=0;
for (var i=data.length; i--;) {
count+=data[i];
if(count >= 1) {
console.log(data);
}
};
requestAnimationFrame(function() {
setTimeout(update, 5000);
});
}
I used setTimeout() in preference over setInterval() as doing it this way (as well as moving the call to the end of the function) will make sure everything is completed, before starting the 5 second pause. It ensures there's no overlap, should the preceeding code take longer than 5 seconds.
First create a variable to store the time of the last console.log. Next, update that variable each time you console.log a value. Finally, add a check for the threshold.
var lastOutput = 0; // Setting this to 0 initially will ensure it runs immediately
var outputThreshold = 500; // in milliseconds
function update() {
requestAnimationFrame(update);
analyser.getByteFrequencyData(data);
if (new Date().valueOf() - lastOutput > outputThreshold) {
// threshold met, output and update
var count=0;
for (var i=data.length; i--;) {
count+=data[i];
if(count >= 1) {
console.log(data);
}
};
lastOutput = new Date().valueOf();
}
}
update(); // fire first call to update, after that requestAnimationFrame() will handle future calls
If you want the time delay inside the for loop, you'd do this:
function update() {
requestAnimationFrame(update);
analyser.getByteFrequencyData(data);
var i = data.length - 1, count = 0;
function logger() {
count += data[i];
if (count >= 1)
console.log(data);
if (i-- >= 0)
setTimeout(logger, 500);
}
logger();
}
Now, things are going to be pretty messy because you're also using requestAnimationFrame() to schedule another iteration of the whole thing; that really won't make sense anymore. You'll probably want to have that wait until the logging process is done:
function update() {
analyser.getByteFrequencyData(data);
var i = data.length - 1, count = 0;
function logger() {
count += data[i];
if (count >= 1)
console.log(data);
if (i-- >= 0)
setTimeout(logger, 500);
else
requestAnimationFrame(update);
}
logger();
}
I have the following for loop:
for (var i = tileLog.length - 1; i >= 0; i--) {
$('.' + tileLog[i]).mouseenter();
};
1 < tileLog.legth < 1025
Is there a way to delay each iteration of the loop so that mouseenter() is triggered every x miliseconds?
I have tried:
function doSetTimeout(i) {
setTimeout(function() { $('.' + i).mouseenter(); }, 250);
}
for (var i = tileLog.length - 1; i >= 0; i--)
doSetTimeout(tileLog[i]);
This doesn't seem to work, it just delays by 250ms then iterates through the loop
As an alternative to using setTimeout() you could also use setInterval().
Define a running variable in the outer scope (like your running i in the loop).
In each iteration, besides calling your function, decrement the running variable. If it is below zero, stop the setInterval()`` :
var index = tileLog.length - 1,
timer = setInterval( function(){
$('.' + tileLog[index]).mouseenter();
index -= 1;
if ( index < 0 ) {
clearInterval( timer );
}
}, 250 );
There is no actual sleep() function or something similar. Would also be problematic as JavaScript (for most cases) is single threaded and such a method would block the render thread, thus rendering your browser inaccessible.
There is no sleep or such in JavaScript. So your approach with timeout is correct.
var tileLog;
var i = titleLog.length - 1;
function func1() {
$('.' + tileLog[i]).mouseenter();
if (--i) {
window.setTimeout(func1, 250);
}
}
// and of course start the process
func1();
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);
I'm currently trying to wrap my head around some JavaScript.
What I want is a text to be printed on the screen followed by a count to a given number, like so:
"Test"
[1 sec. pause]
"1"
[1 sec. pause]
"2"
[1 sec. pause]
"3"
This is my JS:
$(document).ready(function() {
var initMessage = "Test";
var numberCount = 4;
function count(){
writeNumber = $("#target");
setTimeout(function(){
writeNumber.html(initMessage);
},1000);
for (var i=1; i < numberCount; i++) {
setTimeout(function(){
writeNumber.html(i.toString());
},1000+1000*i)};
};
count();
});
This is my markup:
<span id="target"></span>
When I render the page, all I get is "Test" followed by "4".
I'm no JavaScript genius, so the solution could be fairly easy. Any hints on what is wrong is highly appreciated.
You can play around with my example here: http://jsfiddle.net/JSe3H/1/
You have a variable scope problem. The counter (i) inside the loop is only scoped to the count function. By the time the loop has finished executing, is value is 4. This affects every setTimeout function, which is why you only ever see "4".
I would rewrite it like this:
function createTimer(number, writeNumber) {
setTimeout(function() {
writeNumber.html(number.toString());
}, 1000 + 1000 * number)
}
function count(initMessage, numberCount) {
var writeNumber = $("#target");
setTimeout(function() {
writeNumber.html(initMessage);
}, 1000);
for (var i = 1; i < numberCount; i++) {
createTimer(i, writeNumber);
}
}
$(document).ready(function() {
var initMessage = "Test";
var numberCount = 4;
count(initMessage, numberCount);
});
The createTimer function ensures that the variable inside the loop is "captured" with the new scope that createTimer provides.
Updated Example: http://jsfiddle.net/3wZEG/
Also check out these related questions:
What's going on under the hood here? Javascript timer within a loop
JavaScript closure inside loops – simple practical example
In your example, you're saying "2, 3, 4 and 5 seconds from now, respectively, write the value of i". Your for-loop will have passed all iterations, and set the value of i to 4, long before the first two seconds have passed.
You need to create a closure in which the value of what you're trying to write is preserved. Something like this:
for(var i = 1; i < numberCount; i++) {
setTimeout((function(x) {
return function() {
writeNumber.html(x.toString());
}
})(i),1000+1000*i)};
}
Another method entirely would be something like this:
var i = 0;
var numberCount = 4;
// repeat this every 1000 ms
var counter = window.setInterval(function() {
writeNumber.html( (++i).toString() );
// when i = 4, stop repeating
if(i == numberCount)
window.clearInterval(counter);
}, 1000);
Hope this helps:
var c=0;
var t;
var timer_is_on=0;
function timedCount()
{
document.getElementById('target').value=c;
c=c+1;
t=setTimeout("timedCount()",1000);
}
function doTimer()
{
if (!timer_is_on)
{
timer_is_on=1;
timedCount();
}
}