Simply put...
why does
setTimeout('playNote('+currentaudio.id+', '+noteTime+')', delay);
work perfectly, calling the function after the the specified delay, but
setTimeout(playNote(currentaudio.id,noteTime), delay);
calls the function playNote all at the same time?
(these setTimeout()s are in a for loop)
or, if my explanation is too hard to read, what is the difference between the two functions?
The first form that you list works, since it will evaluate a string at the end of delay. Using eval() is generally not a good idea, so you should avoid this.
The second method doesn't work, since you immediately execute a function object with the function call operator (). What ends up happening is that playNote is executed immediately if you use the form playNote(...), so nothing will happen at the end of the delay.
Instead, you have to pass an anonymous function to setTimeout, so the correct form is:
setTimeout(function() { playNote(currentaudio.id,noteTime) }, delay);
Note that you are passing setTimeout an entire function expression, so it will hold on to the anonymous function and only execute it at the end of the delay.
You can also pass setTimeout a reference, since a reference isn't executed immediately, but then you can't pass arguments:
setTimeout(playNote, delay);
Note:
For repeated events you can use setInterval() and you can set setInterval() to a variable and use the variable to stop the interval with clearInterval().
You say you use setTimeout() in a for loop. In many situations, it is better to use setTimeout() in a recursive function. This is because in a for loop, the variables used in the setTimeout() will not be the variables as they were when setTimeout() began, but the variables as they are after the delay when the function is fired.
Just use a recursive function to sidestep this entire problem.
Using recursion to deal with variable delay times:
// Set original delay
var delay = 500;
// Call the function for the first time, to begin the recursion.
playNote(xxx, yyy);
// The recursive function
function playNote(theId, theTime)
{
// Do whatever has to be done
// ...
// Have the function call itself again after a delay, if necessary
// you can modify the arguments that you use here. As an
// example I add 20 to theTime each time. You can also modify
// the delay. I add 1/2 a second to the delay each time as an example.
// You can use a condition to continue or stop the recursion
delay += 500;
if (condition)
{ setTimeout(function() { playNote(theID, theTime + 20) }, delay); }
}
Try this.
setTimeout(function() { playNote(currentaudio.id,noteTime) }, delay);
Don't use string-timeouts. It's effective an eval, which is a Bad Thing. It works because it's converting currentaudio.id and noteTime to the string representations of themselves and hiding it in the code. This only works as long as those values have toString()s that generate JavaScript literal syntax that will recreate the value, which is true for Number but not for much else.
setTimeout(playNote(currentaudio.id, noteTime), delay);
that's a function call. playNote is called immediately and the returned result of the function (probably undefined) is passed to setTimeout(), not what you want.
As other answers mention, you can use an inline function expression with a closure to reference currentaudio and noteTime:
setTimeout(function() {
playNote(currentaudio.id, noteTime);
}, delay);
However, if you're in a loop and currentaudio or noteTime is different each time around the loop, you've got the Closure Loop Problem: the same variable will be referenced in every timeout, so when they're called you'll get the same value each time, the value that was left in the variable when the loop finished earlier.
You can work around this with another closure, taking a copy of the variable's value for each iteration of the loop:
setTimeout(function() {
return function(currentaudio, noteTime) {
playNote(currentaudio.id, noteTime);
};
}(currentaudio, noteTime), delay);
but this is getting a bit ugly now. Better is Function#bind, which will partially-apply a function for you:
setTimeout(playNote.bind(window, currentaudio.id, noteTime), delay);
(window is for setting the value of this inside the function, which is a feature of bind() you don't need here.)
However this is an ECMAScript Fifth Edition feature which not all browsers support yet. So if you want to use it you have to first hack in support, eg.:
// Make ECMA262-5 Function#bind work on older browsers
//
if (!('bind' in Function.prototype)) {
Function.prototype.bind= function(owner) {
var that= this;
if (arguments.length<=1) {
return function() {
return that.apply(owner, arguments);
};
} else {
var args= Array.prototype.slice.call(arguments, 1);
return function() {
return that.apply(owner, arguments.length===0? args : args.concat(Array.prototype.slice.call(arguments)));
};
}
};
}
I literally created an account on this site to comment on Peter Ajtai's answer (currently highest voted), only to discover that you require 50 rep (whatever that is) to comment, so I'll do it as an answer since it's probably worth pointing out a couple things.
In his answer, he states the following:
You can also pass setTimeout a reference, since a reference isn't executed immediately, but then you can't pass arguments:
setTimeout(playNote, delay);
This isn't true. After giving setTimeout a function reference and delay amount, any additional arguments are parsed as arguments for the referenced function. The below would be better than wrapping a function call in a function.
setTimeout(playNote, delay, currentaudio.id, noteTime)
Always consult the docs.
That said, as Peter points out, a recursive function would be a good idea if you want to vary the delay between each playNote(), or consider using setInterval() if you want there to be the same delay between each playNote().
Also worth noting that if you want to parse the i of your for loop into a setTimeout(), you need to wrap it in a function, as detailed here.
It may help to understand when javascript executes code, and when it waits to execute something:
let foo2 = function foo(bar=baz()){ console.log(bar); return bar()}
The first thing javascript executes is the function constructor, and creates a function object. You can use either the function keyword syntax or the => syntax, and you get similar (but not identical) results.
The function just created is then assigned to the variable foo2
At this point nothing else has been run: no other functions called (neither baz nor bar, no values looked up, etc. However, the syntax has been checked inside the function.
If you were to pass foo or foo2 to setTimeout then after the timeout, it would call the function, the same as if you did foo(). (notice that no args are passed to foo. This is because setTimeout doesn't by default pass arguments, although it can, but those arguments get evaluated before the timeout expires, not when it expires.)
After foo is called, default arguments are evaluated. Since we called foo without passing arguments, the default for bar is evaluated. (This would not have happened if we passed an argument)
While evaluating the default argument for bar, first javascript looks for a variable named baz. If it finds one, it then tries to call it as a function. If that works, it saves the return value to bar.
Now the main body of the function is evaluated:
Javascript looks up the variable bar and then calls console.log with the result. This does not call bar. However, if it was instead called as bar(), then bar would run first, and then the return value of bar() would be passed to console.log instead. Notice that javascript gets the values of the arguments to a function it is calling before it calls the function, and even before it looks up the function to see if it exists and is indeed a function.
Javascript again looks up bar, and then it tries to call it as a function. If that works, the value is returned as the result of foo()
So, function bodies and default arguments are not called immediately, but everything else is. Similarly, if you do a function call (i.e. ()), then that function is executed immediately as well. However, you aren't required to call a function. Leaving off the parentheses will allow you to pass that function around and call it later. The downside of that, though, is that you can't specify the arguments you want the function to be called with. Also, javascript does everything inside the function parentheses before it calls the function or looks up the variable the function is stored in.
Because the second one you're telling it to call the playNote function first and then pass the return value from it to setTimeout.
I've tried what seems like dozens of different solutions to this all with different failures. Basically I've built an array that has a bunch of IDs in it and I and using javascript to loop through them and open pages that go through an archiving process. These need to be delayed because it takes about a minute for each one to occur. Here's what I have currently:
var i = 0
function openPage() {
while (i < Array.length) {
window.setTimeout(go(i), 60000*i;);
i++;
}
}
function go(i) {
window.open('http://localhost:12575/DD7RO2.aspx?id=' + Array[i][0]);
}
Then I call openPage from a button press.
For some reason this throws an error of "Invalid argument" at the window.setTimeout line. I can't seem to figure out why that is. Every other iteration of this that I've tried has either opened a single window (the first in the array) and stopped, or opened everything completely disregarding the timeout. I feel like this one might actually work since it's actually doing something different. Any help would be be greatly appreciated.
You are evaluating go(i) when it's passed as an argument. That function executes in the loop. Instead, you should return a function object, and that will be executed when the timer fires.
Do not do function(){go(i);}, that's a classic closure error. You would end up calling the functiongo(i) each time the event fired, but with the same, final, value of i. The scope of i is the enclosing function, so by the time the timeouts run it will have a value of Array.length - 1.
Instead something like
window.setTimeout((function (j){return function(j){go(j)}})(i), 60000*i;);
To spell that out;
(
function (j) {
var k = j;
return function() {
go(k)
}
}
)(i);
This looks confusing, but that's the way JS works.
It immediately executes a function with the value of i.
That returns a function object, but that function is in a closure in which the value of i bound at that point in time.
The function object is executed when the timeout event fires, with the value of i bound correctly.
window.setTimeout(function(){ go(i); }, 60000*i;);
Here you go!
I have a little code using jquery and I want to wait like, 300ms and then change a variable. I tried setTimeout but it wouldn't work, it just made the variable change instantly.
setTimeout(animationWait = 0, 300);
(I defined animationWait earlier globally in the document) Basically what I'm trying to do is wait for a click to end before another click can be done. So i thought I'd set a variable and then wait 300ms,
$('#up-arrow').live('click', function(e) {
if(animationWait == 0) {
animationWait = 1;
.... /* Code */
}
}
So I need to change the animationWait back to 0 again after a delay to run the code. I've tried a bunch of stuff but it's still not working, any ideas?
You are not using setTimeout quite right. It must be passed either a function name or an anonymous function.
//anonymous function
setTimeout( function() { animationWait = 0 }, 300);
//or give it a function name
function change() { animationWait = 0; }
setTimeout(change, 300);
The parameter what you pass should be an evaluation statement. For me this works fine:
var animationWait = -1; //Initially set the value to -1
alert(animationWait); //alert to see the value
setTimeout("animationWait = 0", 300); //Setting the value to 0
setTimeout("alert(animationWait)", 400); //Alert after 400 ms
The answer to your question as asked is that setTimeout() expects the first parameter to be a function reference - either the name of a function declared elsewhere or an anonymous function. (Or a string, but for several reasons using a string is almost always the wrong solution.) Your code as is simply executes the expression animationWait = 0 and passes the result of that expression (0) to setTimeout().
So to execute your single line of code via setTimeout() I'd recommend wrapping it in an anonymous function and passing that function to setTimeout():
setTimeout(function(){ animationWait = 0; }, 300);
However, if you are doing animation with jQuery its animation methods allow you to pass a callback function to be executed when the animation ends, so you can do this type of processing there rather than independently setting your own timeout. For example, if your animation was like this:
$("#someelement").animate({someproperty:"somevalue"},
300);
Then you could add a callback function that will be called after this animation completes:
$("#someelement").animate(({someproperty:"somevalue"},
300,
function(){ animationWait = 0; });
To my eye this is neater than a separate setTimeout() call, but also it is easier to maintain because the duration only has to be specified in one place rather than you having to sync your own timeout to the animation.
A similar syntax applies to other jQuery methods like .fadeIn(), .hide(), etc. See the .animate() doco for more information and examples.
Have you tried using jQuery function delay() ?
A standard use case would look something like this:
$('item').delay(300).delayedEffectFunction();
Where delayedEffectFunction() is a made-up function you want to be delayed.
Why when calling fadeIn() onLoad does the browser run through the loop immediately. In other words there is an issue with either the setInterval or the Opacityto().
function Opacityto(elem,v){
elem.style.opacity = v/100;
elem.style.MozOpacity = v/100;
elem.style.KhtmlOpacity = v/100;
elem.style.filter=" alpha(opacity ="+v+")";}
function fadeIn(){
elem=document.getElementById('nav');
for (i=0;i==100;i++){
setInterval(Opacityto(elem,i),100);}
}
I think someone will tell me this can be done easiest with jQuery but I'm interested in doing it with javascript.
Thanks!HelP!
You've got several problems with your fadeIn() function:
A. Your for loop condition is i==100, which is not true on the first iteration and thus the body of the for loop will never be executed (the i++ won't ever happen). Perhaps you meant i<=100 or i<100 (depending on whether you want the loop to run 101 or 100 times)?
B. Your setInterval code has a syntax error EDIT: since you've updated your question to remove the quotes - setInterval expects either a string or a function reference/expression. So you need to either pass it the name of a function without parentheses and parameters, or a function expression like the anonymous function expression you can see in my code down below. in the way you try to build the string you are passing it. You've got this:
"Opacityto("+elem,i+")"
but you need this:
"Opacityto(elem," + i + ")"
The latter produces a string that, depending on i, looks like "Opacityto(elem,0)", i.e., it produces a valid piece of JavaScript that will call the Opacityto() function.
C. You probably want setTimeout() rather than setInterval(), because setInterval() will run your Opacityto() function every 100ms forever, while setTimeout() will run Opacityto() exactly once after the 100ms delay. Given that you are calling it in a loop I'm sure you don't really want to call setInterval() 100 times to cause your function Opacityto() to be run 100 times every 100ms forever.
D. Even fixing all of the above, your basic structure will not do what you want. When you call setInterval() or setTimeout() it does not pause execution of the current block of code. So the entire for loop will run and create all of your intervals/timeouts at once, and then when the 100ms is up they'll all be triggered more or less at once. If your intention is to gradually change the opacity with each change happening every 100ms then you need the following code (or some variation thereon):
function fadeIn(i){
// if called with no i parameter value initialise i
if (typeof i === "undefined") {
i = -1;
}
if (++i <= 100) {
Opacityto(document.getElementById('nav'), i);
setTimeout(function() { fadeIn(i); }, 100);
}
}
// kick it off:
fadeIn();
What the above does is defines fadeIn() and then calls it passing no parameter. The function checks if i is undefined and if so sets it to -1 (which is what happens if you call it without passing a parameter). Then it increments i and checks if the result is less than or equal to 100 and if so calls Opacityto() passing a reference to the element and i. Then it uses setTimeout() to call itself in 100ms time, passing the current i through. Because the setTimeout() is inside the if test, once i gets big enough the function stops setting timeouts and the whole process ends.
There are several other ways you could implement this, but that's just the first that happened as I started typing...
My guess is that there is a nasty comma inside the setInterval, messing the argument list:
"Opacityto("+elem,i+")"
^^^
here
You could try quoting the comma
+ "," +
but eval is evil so don't do that. The good way is to pass a real callback function:
function make_opacity_setter(elem, i){
return function(){
OpacityTo(elem, i);
};
}
...
setTimeout( make_opacity_setter(elem, i), 1000);
Do note that the intermediate function-making-function is needed to avoid the nasty interaction between closures and for-loops.
BTW, when you do
setInterval(func(), 1000)
you call func once yourself and then pass its return value to setInterval. since setInterval receives a junk value instead of a callback it won't work as you want to.
I have the following code which calls another function, i.e.:
$('input[name='f01']:checked').each(function() {
setCBCollection(this);
});
How can I put a delay of say 2 seconds on each call to setCBCollection(this)?
Using setTimeout:
$('input[name="f01"]:checked').each(function() {
var element = this;
setTimeout(function() {
setCBCollection(element);
}, 2000);
});
setTimeout schedules a function to be called N milliseconds later (roughly, these things are not precise).
Note that we grab this to a variable local to the event handler function, and then the function we pass into setTimeout is a closure over that variable (because otherwise, the meaning of this will get lost). More details:
Closures are not complicated
You must remember this
Off-topic: There's a syntax error in your original, you're using ' within a '-quoted string without escaping it. I changed it to " in my code above.