This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
javascript for loop unexpected behaviour
I'am programming in node.js and i run into a small problem what is caused by some strange pass by reference problem.
i use the following code:
for(j in groups){
console.log(j, somefunction(groups[j]))
}
inside of the function some funky things are done with the data but afterwards i get the following as result:
3 data
3 data
3 data
3 data
3 data
3 data
instead of
1 data
2 data
3 data
4 data
5 data
6 data
but it keeps the correct amount of result..
Your quoted code doesn't do what you've listed, you probably lost something simplifying it for the question.
This code would have the effect you're describing, though:
for(j in groups){
doSomething(function() {
// Callback for `doSomething`
console.log(j, somefunction(groups[j]));
});
}
Note that now I'm creating a function (a callback for whatever doSomething does), not just calling one.
The reason that happens is that the function being created has an enduring reference to the j variable (and everything else in the execution context), not a copy of it as of when the function was created. It's called a closure over that execution context. So your loop runs, a series of calls to doSomething start a series of things, and then later when the callbacks are called, j is 3 and so it's the same for all of them.
The usual solution is to create functions that close over something that won't change before they get called, like this:
for(j in groups){
doSomething(makeCallback(j));
}
function makeCallback(jarg) {
return function() {
console.log(jarg, somefunction(groups[jarg]));
};
}
Now, we're using jarg in our callback, rather than j. Because jarg is part of the context of the call to makeCallback, it doesn't change between when we create the function and when it's called later.
Another way you can bind data to functions is to use Function#bind (which effectively creates a closure in the background), which is an ES5 feature available in NodeJS because the V8 engine has it. Here's how that would look:
for(j in groups){
doSomething(function(jarg) {
// Callback for `doSomething`
console.log(jarg, somefunction(groups[jarg]));
}.bind(this, j)); // <== Note the bind
}
or less confusingly (and quite possibly more efficiently):
for(j in groups){
doSomething(handler.bind(this, j));
}
function handler(jarg) {
// Callback for `doSomething`
console.log(jarg, somefunction(groups[jarg]));
}
More about closures:
Closures are not complicated
Do it this way :-
for(var j in groups)
{
//Your code
}
j is not returning the correct value because you have assigned j at a global scope. Hope this help!
ive found if i use
var j = 0
for(i in groups){
j = i
the system does work how but it's not the best awnser
Related
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 4 years ago.
I am trying to call a promise inside a for loop. I need the call to have the relevant loop item. I read a bit about it but still can't seem to figure the correct way to do it.
var params;
var identifylayers =["parcels", "lots", "gasstation"];
for (i = 0; i < identifylayers.length; i++)
{
lname = identifylayers[i];
govmap.intersectFeatures()
.then(function (e)
{
alert( lname);
});
}
Running the function returns: gasstation,gasstation,gasstation
I want it to return: parcels, lots, gasstation
What am I doing wrong?
Its the async devil of JavaScript lurking in your code. :D
Observe it like this -
As soon as your program flow encounter a promise call which here is - intersectFeatures().then(fn (){}), it know that this call will eventually finish so the program flow will never attempt to execute the callback passed to .then() right at the moment instead it will keep it safe with itself and set a reminder that as soon as the intersectFeatures is finished it need to execute the callback passed to .then().
Next thing to notice is how the .then(callback) will have access to lname. Its by closures. So
Another important thing to notice is that this whole stuff is inside a loop. Your loop has no idea whether the code its iterating on is synchronous or asynchronous. Its only task is to iterate. And as we mentioned before in Point - 1 the .then(callback) will not be executed right at the moment. So by the time then .then(callback) will be executed your loop would already be finished and at the end of iteration lname holds gasstation. Hence your .then(callback) only print gasstation
Solution:
This is really old JS solution of iterating the async code and still ensuring the proper variable access using closures -
for (i = 0; i < identifylayers.length; i++)
{
lname = identifylayers[i];
(function (lname) {
govmap.intersectFeatures()
.then( function () {
alert(lname);
} )
})(lname);
}
This utilises the benefit of functional scope of a variable and idea of closures. Google it to find more information.
Its also referred to as befriending closures, I just made that up :D
Cheers!
Define lname in the loop. Currently it is the same instance for each iteration, so it gets overriden, and then the promise resolves and prints the last overwrite each time.
const lname = identifylayers[i];
It is getting overriden before the async code runs.
async function intersectFeatures() {
return "OK"
}
var params;
var identifylayers = ["parcels", "lots", "gasstation"];
for (let i = 0; i < identifylayers.length; i++) {
const lname = identifylayers[i];
intersectFeatures()
.then(function(e) {
alert(lname);
});
}
Consider the following problem: I want to create an array of functions, each function just prints its index in that array. In Python, it can be done easily with
funcs = []
for i in range(5):
funcs.append(lambda i=i: print(i))
funcs[2]()
# 2
Here we use default argument values as a way to do currying (if I understand the term right).
Prior to ES6, there were no default argument values in Javascript, so currying had to be done in different way. Now we have them and I tried to translate Python to Javascript literally:
funcs = []
for (var i=0; i<5; i++) {
funcs.push(function (i=i) {console.log(i)})
}
# this part pass OK
funcs[2]()
ReferenceError: i is not defined
at Array.<anonymous> (evalmachine.<anonymous>:3:27)
at evalmachine.<anonymous>:1:9
at ContextifyScript.Script.runInThisContext (vm.js:26:33)
at Object.exports.runInThisContext (vm.js:79:17)
at run ([eval]:608:19)
at onRunRequest ([eval]:379:22)
at onMessage ([eval]:347:17)
at emitTwo (events.js:106:13)
at process.emit (events.js:191:7)
at process.nextTick (internal/child_process.js:752:12)
Why it fails? What's the difference between Python and Javascript ways to pass default values?
(Okay, I know that I can use let here instead of var, I'm just studying Javascript after several years with Python and trying to understand it underhoods.)
Your issues have to do with the difference between when default parameters get bound in closures in python vs JavaScript. While it is true that both JavaScript and Python use late-binding, in the case of default parameters, Python simulates early-binding, whereas JavaScript does not.
That being said, if you are going to be creating closures like this, you may as well take advantage of them and ditch the parameters all together, honestly.
You mentioned the use of let and that is important if you want to define the function inside the for loop because otherwise funcs[n] will always be your maximum value of your iterator (due to the late-binding of JavaScript closures).
Try this:
funcs = [];
for (let i=0; i<5; i++) {
funcs.push(function () {console.log(i)});
}
funcs[2]();
Alternatively, if you want to follow good practice of not defining functions within a loop, you can define the function outside, and pass the variable in using .bind(). One thing to note is that this method will bind the variable with the value at the time .bind() is called, so you do not have to use let
funcs = [];
function myFunc(i) {
console.log(i);
}
for (var i=0; i<5; i++) {
funcs.push(myFunc.bind(this, i));
}
funcs[2]();
Both Javascript and Python using late-binding in closures. However, using a default argument is a hack that let's you simulate early binding in Python, and that works because default parameters are evaluated at function definition time in Python. However, in Javascript default arguments, according to the docs
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters
The default argument gets evaluated at call time, so unlike e.g. in
Python, a new object is created each time the function is called.
Here is one of the solutions used in Python that can be applied to this problem in Javascript. I've transliterated into Javascript the best I could. Essentially, define another anonymous function that returns your original, and apply it at the same time. This is very messy to my eyes, and in Python, I always go with the default-argument:
funcs = []
for (var i = 0; i < 5; i++) {
funcs.push((function (i) {return function() {console.log(i)}})(i))
};
funcs[0]() // 0
funcs[4]() // 4
In Python:
>>> funcs = []
>>> for i in range(5):
... funcs.append((lambda i: lambda : print(i))())
...
>>> funcs[0]()
0
>>> funcs[4]()
4
I think it is clear that you should use .bind in Javascript, as elucidated in other answers, and not this clunky solution.
If you use let instead of var you get a new binding for each iteration.
funcs = []
for (let i=0; i<5; i++) {
funcs.push(function (j=i) {console.log(j)})
}
funcs[2];//2
i=i doesn't work because in ES6 parameters can define defaults using other parameters
f = function(a=1, b=a){console.log(b);}
f() // 1
So the parser is getting confused.
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 6 years ago.
I am seeking help understanding why the way I am using anonymous functions are erroring in some circumstances.
In the snippet below I popuplate 2 arrays with functions which are invoked at a later stage.
var arr1 = [];
var arr2 = [];
// callback function
var func = function(i){
return function(){$("body").append(i);}
};
var i = 1;
while(i <= 5)
{
arr1.push(function(){$("body").append(i);});
arr2.push(func(i));
i++;
}
// invoke functions
$("body").append("Output from arr1 == ");
for(var c = 0; c < arr1.length; c++){ arr1[c](); }
$("body").append("<br> Output from arr2 == ");
for(var c = 0; c < arr1.length; c++){ arr2[c](); }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Now, I think I understand why arr1 outputs 66666, because at the time of invocation, i == 6 and becuase it has not been stored as a param within that function at time of creation, i retains it's last known value?
What I really don't understand is why i recieve TypeError: arr2[c] is not a function When I change the callback function to:
var func = function(i){
$("body").append(i);
};
Why does this happen and is this the most appropriate / elegant way to achieve this functionality.
For the first part of the question, you are right, it will retain the latest known value. To avoid that, you need to use closure. For example:
(function(x) {
arr2.push(func(x));
})(i);
Closure can often be used as a "scope hack". In this case, it will only ensure that the injected value is the constant current value of i.
For the second part of the question, your confusion is probably caused by the fact that you insert the result of the function (i.e.: func(i)) instead of the function itself (func).
In the first scenario, you return an explicit function(){...} so your array will contain a function that you can call with operator "()".
In the second scenario, in which you return $(<...>).append(), it's not guaranteed anymore that your return value is a function. In that case, the result is "jQuery" (see: jQuery append) and not a function, hence the error is correct. You cannot use the value as a function.
One way out is:
arr2.push(func);
Then:
arr2[c](c);
Or probably this:
(function(x){
arr2[x](x);
})(c);
If needed, it can be helpful to always console.log what is populated inside your arrays before assuming it right. Since Javascript is not a typed language, you may go for a little while before suspecting anything wrong as it will seem work as intended.
Hope this helps!
i was a reading book named async javascript , one of the example of this book tried to show that events in javascript will trigger when processor has no instruction to do in the same process . for the proof of his word he brought an example :
for (var i = 1; i <= 3; i++) {
setTimeout(function(){ console.log(i); }, 0);
};
the output is :
4
4
4
4
everyhting is ok and i expected the result . but when i put the "console.log(i)" out of function the result will change .
for (var i = 1; i <= 3; i++) {
setTimeout(console.log(i), 0);
};
the output is :
1
2
3
i don't know why the output is different in those examples . i know its about variable scopes , but don't know exactly the reason . i hope if someone can help.
i don't know why the output is different in those examples
Because in the second case you are calling console.log(i) immediately and pass the return value undefined to setTimeout (instead of passing a function like in the first case).
Arguments are always evaluated first, so if you have foo(bar()), bar is called first and its return value is passed to foo.
You can verify this by adding console.log('after'); after the loop. In the first case, you should get
after
4
4
4
and in the second case, you will get
1
2
3
after
demonstrating that setTimeout is not adding anything to the event queue.
See also Calling functions with setTimeout()
i know its about variable scopes
Actually it's not, it's all about timing, the moment when console.log is executed.
When you call setTimeout(expr,t), expr gets evaluated, hoping it returns a function. In your second case console.log(...) doesn't return anything, but it gets evaluated nonetheless. What you see is the side-effect of this evaluation prior to passing the result to setTimeout.
I am trying to utilize Google's AJAX Language API to translate each value in an array.
for(var n=0; n < mytext.length; n++) {
google.language.translate(mytext[n], originalLanguage, newLanguage, function(result){
if(!result.error){
document.getElementById("caption") += mytext[n]+" has been translated to "+result.translation;
}
})
}
This correctly translates the entire array, but in the success function called by google.language.translate, n is always equal mycaptions.length. This results in mycaptions[n] returning as undefined (e.g., " has been translated to Hola"). This has been bewildering me for days (why is the value of n inside the callback function always as if you are at the end of the loop???), and I am guessing the answer lies in an obvious bit of programming I just don't get.
This has to do with how closures work in JavaScript; when JavaScript creates a closure, any variables that get used are referenced, rather than copied, so when you construct the anonymous function, it stores a reference to n rather than copying the value of n. Hence, when it actually gets called, it runs with the current value of n (which is the value that gets assigned to it at the end of the loop). The workaround is to create a function that takes a parameter n and returns a closure:
function createSuccessFunction(n) {
return function() {
// behavior on success
};
}
// use createSuccessFunction(n) where you need a callback