setTimeout giving arguments to function from another function - javascript

Got a problem, don't know how can I give to ClickSimClick function arguments exposed by ClickSimMove func (it returns array with 2 values).
Code below says that crd is undefined on setTimeout.
var crd = plugin().ClickSimMove();
setTimeout("plugin().ClickSimClick(crd[0], crd[1])", 1000);

Pass a function, not a string:
var crd = plugin().ClickSimMove();
setTimeout(function() {
plugin().ClickSimClick(crd[0], crd[1]);
}, 1000);
When you pass a string, it's evaluated as it would be with eval in the global scope, losing all access to local variables. An anonymous function lets you reference any variable in scope.

var crd = plugin().ClickSimMove();
setTimeout(function(){
plugin().ClickSimClick(crd[0], crd[1]);
}, 1e3);
When at all possible, avoid sending strings to setTimeout/setInterval--use an anonymous function instead. Especially if you find yourself concatenating variables to make that string, you can run in to trouble very quickly with some sort of injection or malformed component.

Related

Execute private function inside the class by its name (string)

At the moment I have simple JavaScript class like this:
function MyClass() {
// ... some code ...
this.Create = function() {
funcName = 'myTestFunc()';
cTimer = setTimeout(funcName, 1000);
}
// ... more code ...
var myTestFunc = function() {
alert ('everything\'s OK!');
}
// ... more code ...
}
and to test it I'm using this code:
x = new MyClass();
x.Create();
I have some troubles to execute this function by it's name. If I put just eval(funcName); instead of setTimeout call it works fine but can't figure out why it doesn't work this way.
Course, this is part of more complex code but rest of code is irrelevant to this problem.
My question is obvious - How to execute function by its name set as setTimeout function's argument? Is it possible?
Note: Making this function public (this.myTestFunc = ...) isn't an option!
Update:
funcName = "myTestFunc()"; is just an example. In real code it looks like funcName = getRandomEffectFunctionName();! It's just a random value.
Referring to the update:
Instead of setting:
var funcName = "getRandomEffectFunctionNeme()";
Thus, setting a reference to the function's name you should do
var funcRef = getRandomEffectFunctionNeme;
And set a reference to the function itself . Not only this avoids the issues setTimeout with strings has*. It also solves your closure issue since your code is structured in such a way the timeout has access to the function itself.
In your case, let's assume you have some functions that are filters, for example lowPass highPass and blur. In that case, instead of choosing a function name we would be choosing a function.
First, we store these functions in an array:
var filters = [lowPass,highPass,blur];
In JavaScript, functions are first-class objects, you can pass them around just like other objects.
Next, we'll get a random number
var chosen = Math.floor(Math.random()*3);//get a random number between 0 and 2
Finally, we'll choose the filter and invoke it
var filter = filters[chosen];
setTimeout(filter,1000);
( * just try debugging it, it basically invokes the compiler whenever ran and is painfully slow)
You just pass a function to setTimeout as a parameter, rather then a string, setTimeout(myTestFunc,1000) .
When calling Create it would have access to it anyway because they are in the same closure.
NOTE: This solution is only applicable if you can not pass the function name as a function reference, for example if you're integrating with code that is outside your control. Generally, when possible, you should pass a function reference since in JavaScript, all functions are objects.
Assuming that the timeout and the function are in the same closure your code is pretty close. The problem is that your eval call executes in the global context because it is in a timer. This means they are no longer in the same lexical scope.
You can however, grab a reference to the function by clever use of eval which you can later call in the setTimeout invocation.
var F=eval(funcName);// gain a reference to the function given the function's name
cTimer = setTimeout(F, 1000);
If you're using AIR or don't trust the functionName string you can do the following:
function Test(){
var functionContainer={
t:function(){
console.log("it's t");
}
};
this.callT=function(functionName){
var F=functionContainer[functionName];
console.log("F is:",F);
setTimeout(F,500);
}
}
(new Test()).call("t");
This is preferable since you are invoking setTimeout with a function's name and not a string. In general, using setTimeout with a string can have issues, it's hard to debug or maintain.

Passing variables VS passing values

I would love to understand the difference between:
var the_timeout = setTimeout("alert(the_string);", 60000);
and:
var the_timeout = setTimeout("alert(" + the_string + ");",60000);
I understand that the first passes the variable, and the second passes the value - but what does that mean exactly and why does it matter? Why is the value passed in the second example?
Also (I hope this is the same subject), why does this work:
var timeout=setTimeout("someFunction();", 3000)
While this doesn't:
var timeout=setTimeout(someFunction(),3000);
When calling a function, someFunction() works, so why do I have to add quotes when using setTimeout()?
I think you're confusing the distinction between pass-by-value and pass-by-reference with this. In the examples you mentioned, there is no difference.
However,
var timeout=setTimeout("someFunction();", 3000)
Works and:
var timeout=setTimeout(someFunction(),3000);
Doesn't because in the second case, someFunction() would run so that it can then pass the result/return value to setTimeout. That's why you pass it as a string so that setTimeout can eval it on its own. Unless, of course, if someFunction() itself returns a function that setTimeout can use as a callback.
However, as zerkms pointed out in a comment, you should pass callbacks instead:
var timeout = setTimeout(function() { someFunction(); }, 3000);
This also has the effect that setTimeout can then call the callback whenever it wants. A major benefit is that you can pass any regular function so that you can benefit from the editor you may be using, instead of packing it all into a string:
var myTrigger = function() {
someFunction();
};
var timeout = setTimeout(myTrigger, 3000);
This will parse execute the code within the string, 60 seconds later.
var the_string = "Hello";
setTimeout("alert(the_string);", 60000);
the_string = "Goodbye";
This means alert(the_string) is executed, just as if it was regular code. So it would alert "Goodbye". This is because when the code is eventually executed, the updated value of the_string is used since you are passing the variable.
But this does something subtly different.
var the_string = "Hello";
setTimeout("alert(" + the_string + ");",60000);
the_string = "Goodbye";
Now we are creating a new snippet of code on the fly. The snippet we are creating is alert(Hello);. But Hello is a variable with no value because you didn't get the quotes right.
But lets say you meant this:
var the_string = "Hello";
setTimeout("alert('" + the_string + "');",60000);
the_string = "Goodbye";
Now this will work, because the code it generates is alert('Hello');. Which at first glance appears to do the same thing. But because the generated code now includes literally a hardcoded string, so when the_string changes, the change doesn't make it into generated code because it was hardcoded into the snippet.
Based on that, this is simple:
setTimeout("someFunction();", 3000)
The code in the string is executed after the delay. In this case, someFunction() is executed.
But this is totally different:
setTimeout(someFunction(),3000);
In this case, someFunction() is executed immediately, and it's return value is passed as the first argument to the setTimeout() function. So it won't do what you expect at all.
Most of this has to do with the quirks of eval and generated code, as setTimeout(string,delay) is a form of eval. And none of this is a problem if you don't use eval, and you don't pass a string to setTimeout().
Passing a string to setTimeout leads to eval, eval leads to wierd crazy bugs, wierd crazy bugs lead to pain, pain leads to sufferrriing.
Instead you pass a function instead. It's better in every single way.
// pass an anonymous function to run some code later
var the_string = "Hello";
setTimeout(function() {
alert(the_string);
}, 60000);
the_string = "Goodbye";
// alerts "Goodbye" 60 seconds later
// pass an anonymous function to run some code
setTimeout(function() {
someFunction();
}, 3000);
// or pass a reference to a function to execute, note lack of ()
setTimeout(someFunction, 3000);

setTimeout within function fails

This function accepts an argument, whichImage. This is the object HTMLImageElement for the image we are working with. The image width will be halved, and then after 3 seconds, it will return to normal width. However, the setTimeout code, which is supposed to execute after 3 seconds, fails, with the error message that whichImage is not defined. What do I need to correct to make this function work?
function resizeImageFunction(whichImage){
// Get the width of the image
alert(whichImage);
var width = whichImage.width;
var halfwidth = Math.round(width/2);
whichImage.width=halfwidth;
setTimeout("whichImage.width=width;",3000);
}
function resizeImageFunction(whichImage){
// Get the width of the image
alert(whichImage);
var width = whichImage.width;
var halfwidth = Math.round(width/2);
whichImage.width=halfwidth;
setTimeout(function(){
whichImage.width=width;
},3000);
}
The explanation for your problem is as follows:
When you pass a string to setTimeout(), that string will be evaluated by eval() in the global scope. Thus any function you call or variable you reference there must be available from the global scope. That explains why you can't reference a local variable or an argument to your function because neither or those are in the global scope so when eval() tries to find them, it looks in the global scope and they aren't there.
When you change the setTimeout() function to this using an inline anonymous function:
setTimeout(function() {
whichImage.width = width;
}, 3000);
now you have real javascript code (not a string) that is evaluated in place where it exists with no use of eval() and because of closures you have full access to the local variables and arguments of the enclosing function which gives you access to both whichImage (an argument) and width (a local variable) so your code works.
This is reason #14 which you should always use real javascript function references or anonymous function declarations instead of passing a string to setTimeout().
You need to wrap your block of code in an anonymous function like:
setTimeout(function () {
whichImage.width=width;
}, 3000);
Try:
setTimeout(function() {
whichImage.width=width;
},3000);
You don't need to use eval for this
setTimeout(function() { whichImage.width=width; } ,3000);
Here is is your function
function resizeImageFunction(whichImage){
var halfwidth = Math.round(width/2);
whichImage.width=halfwidth;
setTimeout(function() { whichImage.width=width; } ,3000);
}

setInterval can't access variable?

I have a JavaScript function named showchild(pgid). I have called function on document ready...
$(document).ready(function()
{
var pgid = $('#hiddenuserkey').val();
//alert(pgid);
showchild(pgid);
setInterval("showchild(pgid)",1000);
});
You are using it in the worst possible way - passing a string.
Use the following code instead:
setInterval(function() {
showchild(pgid);
}, 1000);
When passing a string, it will be evaluated in the global context without having access to any non-global variables. By passing a function (the preferred way) all accessible variables are preserved in the function's closure so pgid is defined inside that function when it's called.

Why won't my timer increment the number?

I recently learned javascript. I was experimenting with it. Now, I tried to make a simple timer. Here is the code:
<html>
<head>
<script type="text/javascript">
function start(obj)
{
var t = setTimeout("increment(obj)", 1000);
}
function increment(obj)
{
obj.innerHTML = parseInt(obj.innerHTML) + 1;
start(obj);
}
</script>
</head>
<body>
<p onclick="start(this)">0</p>
</body>
</html>
The contents of the <p></p> should be incremented by 1 every second. Does anyone know why this doesn't work?
Because the string you pass into setTimeout is evaluated at global scope, not the scope within the function, and so doesn't refer to your obj object.
You should never pass strings to setTimeout. Instead, pass it a function reference:
function start(obj)
{
var t = setTimeout(function() {
increment(obj);
}, 1000);
}
function increment(obj)
{
obj.innerHTML = parseInt(obj.innerHTML) + 1;
start(obj);
}
The function we're passing to setTimeout is a closure, which means it has an enduring reference to the items in scope where it's defined. So a second later when the timer mechanism calls it, it still has access to the obj argument of your start function even though the start function has long since returned. More here: Closures are not complicated
The issue (or at least, the first that I see) is that you are passing the string "increment(obj)" to the setTimeout() method, but obj is only defined inside of your start() method. The string that you pass isn't actually evaluated until the timeout triggers, at which point no obj variable is in scope.
There are a few different ways around this. One is to pass a closure to setTimeout() instead of a JavaScript string, like:
function start(obj) {
var nextIncrement = function() {
increment(obj);
};
var t = setTimeout(nextIncrement, 1000);
}
Another (though less preferable) option is to promote obj to the global scope, like:
function start(obj) {
window.obj = obj;
var t = setTimeout("increment(obj)", 1000);
}
In general, however, you should avoid passing a string to setTimeout (and you should also avoid placing things in the global scope unnecessarily). As you have seen, it can cause issues with scope resolution, and for any non-trivial operation it also makes you code much less maintainable. Always prefer passing a function closure when possible.
Also, the following line of code is probably not doing exactly what you expect:
parseInt(obj.innerHTML)
You should always provide the radix argument to parseInt, to avoid errors with values such as 011 (which is 9, rather than 11, because it is evaluated in base-8 due to the leading 0). You can avoid these quirks by simply doing:
parseInt(obj.innerHTML, 10)
...to force a base-10 parse.
Anyways, working example here: http://jsfiddle.net/dSLZG/1
The problem is with this line of code:
var t = setTimeout("increment(obj)", 1000);
obj is an identifier in the functions scope -- it is only accessible within the start function. When you pass a string to setTimeout, it is evaluated in the global scope. This means that the obj variable is not available, so nothing is incremented.
You should pass a function object instead, as this will create a closure and your variable will be accessible:
function start(obj)
{
setTimeout(function() {
increment(obj);
}, 1000);
}
Note that I have removed the unnecessary var t =, because you're not doing anything with the timer identifier.
I've copied your code over to jsFidle and added a working example. See sample code.
The problem in your original version is that your variable obj is not defined in the global context. Whenever you pass strings to setTimeout you'll end up having the string evaluated in the global context (where obj is not a variable).
What you should do instead is never pass a string to setTimeout but a function object. This function object holds references to all variables that are set where the function object was defined.
So, because the function object passed to setTimeout in start2 is defined within start2 it has access to all variables and parameters of start2. Your variable obj is one of those and thus accessible within the function object as soon as it is executed by setTimeout.

Categories

Resources