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.
Related
I know there is an Answer for this But!! All The Answers covered with only one setTimeout in the loop this Question Looks relevant to me How do I add a delay in a JavaScript loop?
But in my Scenario I Have two setTimeout in the Script, How can this be implemented correctly with timing !! The Program works correctly but the timing what I want is not correct !!!
function clickDate(i)
{
setTimeout((function(){
alert("4");
})(),2000);
}
function clickButton(i)
{
setTimeout((function(){
alert("5");
})(),4000);
}
function doEverything(i)
{
clickDate(i);
clickButton(i);
}
for(var i = 0; i < 4; i++)
{
doEverything(i);
}
You're immediately calling the function when you pass it to setTImeout. Remove the extra parenthesis.
function clickDate(i)
{
setTimeout(function(){
alert("4");
},2000);
}
function clickButton(i)
{
setTimeout(function(){
alert("5");
},4000);
}
function doEverything(i)
{
clickDate(i);
clickButton(i);
}
for(var i = 0; i < 4; i++)
{
doEverything(i);
}
EDIT
It's a little unclear what exactly it is you want your code to do seeing as you're passing i into your function I assume you want to use it somehow. Currently you're creating timeouts that will all launch at once. You'll need to stagger the delay times if you want them to launch in sequence. The code below logs a "4" every 2 seconds and a "5" every "4" seconds by multiplying the delay time by i+1.
// Currently this code displays a 4 every 2 seconds and a 5 every 4 seconds
function clickDate(i)
{
setTimeout(function(){
console.log("4");
},2000 * (i+1));
}
function clickButton(i)
{
setTimeout(function(){
console.log("5");
},4000 * (i+1));
}
function doEverything(i)
{
clickDate(i);
clickButton(i);
}
for(var i = 0; i < 4; i++)
{
doEverything(i);
}
Hello I think you havent read documentation about javascript.
It's asynchronous and it will not wait for the event and continue the process. I will give the answer but I highly recommend to read about Javascript it's good for you only here you will get timing problem because your both the function will be called at the same time. Let me give you the example.
function clickDate(i,callback)
{
setTimeout(function(){
alert("4");
callback();//this will call anonymous function in doEverything
},2000);
}
function clickButton(i)
{
setTimeout(function(){
alert("5");
},4000);
}
function doEverything(i)
{
console.log("In loop index is " , i);
clickDate(i,function(){
clickButton(i);
});
//look closely here I have passed the function in changeData and will call that funtion from clickDate
console.log("In loop terminating index is " , i);
}
for(var i = 0; i < 4; i++)
{
doEverything(i);
}
So here console log will make you clear about asynchronous
functionality. You will see that for loop terminates as it continues
it's work and easily completed in 2 seconds so before your first alert
for loop will complete it's iteration.
Hopefully this will help.
you are calling the callback immediately by adding () to the end of function .
you need to pass the callback with timeout and it will be call for you
setTimeout(function(){
alert('hello');
} , 3000);
function functionName() {
setTimeout(function(){ //Your Code }, 3000);
}
Try this one.
Your approach to mocking asynchronous behavior in JavaScript with setTimeout is a relatively common practice. However, providing each function with its own invocation of setTimeout is an anti-pattern that is working against you simply due to the asynchronous nature of JavaScript itself. setTimeout may seem like it's forcing JS to behave in a synchronous way, thus producing the 4 4 4 4 then 5 5 you are seeing on alert with iteration of the for loop. In reality, JS is still behaving asynchronously, but because you've invoked multiple setTimeout instances with callbacks that are defined as anonymous functions and scoped within their own respective function as an enclosure; you are encapsulating control of JS async behavior away from yourself which is forcing the setTimeout's to run in a strictly synchronous manner.
As an alternative approach to dealing with callback's when using setTimeout, first create a method that provides the timing delay you want to occur. Example:
// timer gives us an empty named "placeholder" we can use later
// to reference the setTimeout instance. This is important because
// remember, JS is async. As such, setTimeout can still have methods
// conditionally set to work against it.
let timer
// "delayHandler", defined below, is a function expression which when
// invoked, sets the setTimeout function to the empty "timer" variable
// we defined previously. Setting the callback function as the function
// expression used to encapsulate setTimeout provides extendable control
// for us later on however we may need it. The "n" argument isn't necessary,
// but does provide a nice way in which to set the delay time programmatically
// rather than hard-coding the delay in ms directly in the setTimeout function
// itself.
const delayHandler = n => timer = setTimeout(delayHandler, n)
Then, define the methods intended as handlers for events. As a side-note, to help keep your JS code from getting messy quickly, wrap your event handler methods within one primary parent function. One (old school) way to do this would be to utilize the JS Revealing Module Pattern. Example:
const ParentFunc = step => {
// "Private" function expression for click button event handler.
// Takes only one argument, "step", a.k.a the index
// value provided later in our for loop. Since we want "clickButton"
// to act as the callback to "clickDate", we add the "delayHandler"
// method we created previously in this function expression.
// Doing so ensures that during the for loop, "clickDate" is invoked
// when after, internally, the "clickButton" method is fired as a
// callback. This process of "Bubbling" up from our callback to the parent
// function ensures the desired timing invocation of our "delayHandler"
// method. It's important to note that even though we are getting lost
// in a bit of "callback hell" here, because we globally referenced
// "delayHandler" to the empty "timer" variable we still have control
// over its conditional async behavior.
const clickButton = step => {
console.log(step)
delayHandler(8000)
}
// "Private" function expression for click date event handler
// that takes two arguments. The first is "step", a.k.a the index
// value provided later in our for loop. The second is "cb", a.k.a
// a reference to the function expression we defined above as the
// button click event handler method.
const clickDate = (step, cb) => {
console.log(step)
cb(delayHandler(8000))
}
// Return our "Private" methods as the default public return value
// of "ParentFunc"
return clickDate(step, clickButton(step))
}
Finally, create the for loop. Within the loop, invoke "ParentFunc". This starts the setTimeout instance and will run each time the loop is run. Example:
for(let i = 0; i < 4; i++) {
// Within the for loop, wrap "ParentFunc" in the conditional logic desired
// to stop the setTimeOut function from running further. i.e. if "i" is
// greater than or equal to 2. The time in ms the setTimeOut was set to run
// for will no longer hold true so long as the conditional we want defined
// ever returns true. To stop the setTimeOut method correctly, use the
// "clearTimeout" method; passing in "timer", a.k.a our variable reference
// to the setTimeOut instance, as the single argument needed to do so.
// Thus utilizing JavaScript's inherit async behavior in a "pseudo"
// synchronous way.
if(i >= 2) clearTimeout(timer)
ParentFunc(i)
}
As a final note, though instantiating setTimeOut is common practice in mocking asynchronous behavior, when dealing with initial invocation/execution and all subsequent lifecycle timing of the methods intended to act as the event handlers, defer to utilizing Promises. Using Promises to resolve your event handlers ensures the timing of method(s) execution is relative to the scenario in which they are invoked and is not restricted to the rigid timing behavior defined in something like the "setTimeOut" approach.
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.
Let's say that I want to invoke a function with the onClick method. Like this:
<li class="inline" onclick="mve(this);" >TEMP</li>
AND I have a JS function that looks like this:
function mve(caller){
caller.style.position = "relative";
caller.style.left = (caller.style.left+20) +'px';
var foo = setTimeout('mve(caller)', 2000);
}
My problem is that the element (which caller refers to) is undefined after the initial onClick call. At least this is what Firebug is telling me.
I'm sure it's an easy solution, so how about just a simple explanation of why, and how?
Also if I run it like so:
function mve(caller){
caller.style.position = "relative";
caller.style.left = (caller.style.left+20) +'px';
}
I would think the element would move 20px right on every click, however that is not the case. Thoughts?
setTimeout() executes a string parameter in the global scope so your value of this is no longer present nor is your argument caller. This is one of many reasons NOT to use string parameters with setTimeout. Using an actual javascript function reference like this and it's quite an easy problem to solve getting the arguments passed accordingly:
function mve(caller){
caller.style.position = "relative";
caller.style.left = (caller.style.left+20) +'px';
setTimeout(function() {
mve(caller)
}, 2000);
}
For the second part of your question, caller.style.left is going to have units on it like 20px so when you add 20 to it, you get 20px20 and that's not a value that the browser will understand so nothing happens. You will need to parse the actual number out of it, add 20 to the number, then add the units back on like this:
function mve(caller){
caller.style.position = "relative";
caller.style.left = (parseInt(caller.style.left), 10) +20) + 'px';
setTimeout(function() {
mve(caller)
}, 2000);
}
Something that is missing from this function is a way for it to stop repeating. As you have it now, it goes on forever. I might suggest passing in either a number of iterations like this:
function mve(caller, iterationsRemaining){
caller.style.position = "relative";
caller.style.left = (parseInt(caller.style.left), 10) +20) + 'px';
if (--iterationsRemaining) > 0) {
setTimeout(function() {
mve(caller, iterationsRemaining)
}, 2000);
}
}
Also, you might be curious to know that this isn't really a recursive function. That's because the mve() function calls setTimeout() and then finishes right away. It is setTimeout() that executes the next iteration of mve() some time later and there is no accumulation on the stack frame of multiple function calls and thus no actual recursion. It does look like recursion from a glance at the code, but isn't technically.
It's likely that caller goes out of scope after the first call. You could preserve it by creating a variable with global scope that preserves the value of caller:
var globalCaller;
function onClickEvent(caller) {
globalCaller = caller;
mve();
}
function mve() {
globalCaller.style.position = "relative";
globalCaller.style.left = (caller.style.left+20) +'px';
var foo = setTimeout('mve()', 2000);
}
This is really ugly, though. You'll create much cleaner code by passing the id of the li element, and then calling getElementById() instead. Or better yet, use jQuery and use $("#id") syntax instead.
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 a function called "tryMe" and I'm calling it without the parenthesis not precisely this but the idea is like what you would do here:
setTimeout(tryMe,200);
how can I pass parameters that I need?
I am using a jquery plugin that enables me to call a function but I have to call it withou the parenthesis or it executes itself upon loading.
setTimeout(function() { tryMe(parm1, parm2); }, 200);
A more robust offering, to ensure that the values of parm1, parm2 don't change before the timeout fires (per #lincolnk's comment):
setTimeout(function() {
var p1 = parm1;
var p2 = parm2;
tryMe(p1, p2);
}, 200);
#patrick dw, you're right, has to be evaluated before.
You can wrap tryMe in a closure.
For example:
var f = function(){tryMe('some parameter');};
setTimeout(f, 200);
Here, we create a function object, f, which calls tryMe with the desired parameter(s). Then we pass f to setTimeout. When the timeout expires, f will be called, which will in turn call tryMe with the desired parameters.
A word of warning if you wish to pass in parameters that may change before the timeout gets called (for example, if you are setting several timeouts in a for loop): you will want to bind those variables like so:
var f = function(someParamter){return function(){tryMe(someParameter);};};
setTimeout(f(someParameter), 200);
The reason simply doing something like
setTimeout(tryMe('some parameter'), 200); //Does not work.
doesn't work is because you are passing the result of evaluating tryMe instead of the function object tryMe itself.
You are not calling the function, the browser does - after 200 milliseconds. You are merely providing it with the function that needs being called. Firefox actually allows specifying additional parameters in the setTimeout call:
setTimeout(tryMe, 200, "first parameter", "second parameter");
However, as far as I know no other browsers support this feature so you should really use a closure function as explained by other answers already. Here you create a temporary function with the sole purpose of calling your original function with the right parameters.
setTimeout(function() {tryMe("first parameter", "second parameter")}, 200);