Closure Inside For Loop - javascript

I know that one of the ways to log 0 to 9 with this code:
EDIT: Source
for(var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}), 10)
}
jsfiddle
is to make setTimeout self invoking and pass i as a parameter, like so:
for(var i = 0; i < 10; i++) {
setTimeout((function(i) {
console.log(i);
})(i), 10)
}
but I've tested making setTImeout self invoking without passing i, and it still works:
for(var i = 0; i < 10; i++) {
setTimeout((function() {
console.log(i);
})(), 10)
}
jsfiddle
My questions:
Why does it work even without passing i as a parameter?
Is it necessary to pass i?

Problem
It's not a Closure.
for(var i = 0; i < 10; i++) {
setTimeout((function(i) {
console.log(i);
})(i), 10)
}
setTimeout expects actually a function as parameter, but you made it invoking immediately. So it logs immediately the value of i, without waiting. setTimeout has now the return of your anonymous function as first parameter which is undefined.
The same here:
for(var i = 0; i < 10; i++) {
setTimeout((function() {
console.log(i);
})(), 10)
}
It's executed immediately, it doesn't wait for the 10ms. The only difference you didn't passed i as parameter, but it will look in the parent scope for a variable called i – and there is one.
You will see that your anonymous function is called immediately if you set the time to e.g. one second (1000).
Solution
A real closure would look like this:
Without parameter: You will see 10 times 10, because at time of execution of the inner function the loop is already finished which means i equals 10 at that time:
for(var i = 0; i < 10; i++) {
(function() {
setTimeout(function() {
console.log(i);
},10);
})();
}
With parameter – you will get the expected result:
for(var i = 0; i < 10; i++) {
(function(i) {
setTimeout(function() {
console.log(i);
},10);
})(i);
}

In all your avobe code, you are executing the function given as the first parameter in setTimeout without returning anything. So what happens is you are returning undefined.
In the code below, I am also self-invoking, but returning a function for setTimeout to execute after a specific intervali * 10.
This is the closure function. Check the console logs.It prints the time stamp in miliseconds. There is a gap of 10ms.
for (var i = 0; i < 10; i++) {
setTimeout((function(i) {
return function() {
console.log(i, performance.now() + 'ms');
}
})(i), i * 10)
}
Additional Info
In the comment section, I have a query
what's the point, when you could just put the setTimeout inside an anonymous function?
So I guess this is the code you are suggesting.
for (var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i, performance.now() + 'ms');
}, i * 10)
}
If you run this snippet, you will get a log of 10 for every log. This is the beauty of closures. When you are executing the annonymous function
function() {
console.log(i, performance.now() + 'ms');
}
It cant find i in current scope and searches it parent scope. And voila, it finds i, but unfortunately the loop has completed its iteration by that time and its value = 10 now. So logged 10 every time.
This is why we need closure.

Related

i++ loops without any problem, i+2 loops infinitely and crashes

I want to create a simple loop function that adds 2 every time it loops. However, even though I tell my for loop to stop if the number reaches or is less than 100, it goes past 100 and loops infinitely.
i++ works just fine:
function addTwo() {
for (i = 0; i <= 100; i++) {
console.log(i);
}
}
addTwo();
When I change it to i+2 it crashes:
function addTwo() {
for (i = 0; i <= 100; i + 2) {
console.log(i);
}
}
addTwo();
I expect the console to log:
0
2
4
6
8
...
100.
But instead it loops infinitely and crashes.
i+2 in your case does nothing. JS evaluates it and then does nothing with the calculated value, this means that i is never increased.
++ is a special operator that increments the variable preceding it by 1.
To make the loop work you have to assign the value of the calculation i+2 to the variable i.
for (i=0; i<=100; i = i+2) {
console.log(i);
}
or
for (i=0; i<=100; i += 2) {
console.log(i);
}
i++ increments i. But, i+2 doesn't update the value of i. You should change it to i += 2
function addTwo() {
for (i = 0; i <= 100; i += 2) {
console.log(i);
}
}
addTwo();
The third parameter of a for is the final-expression:
An expression to be evaluated at the end of each loop iteration. This occurs before the next evaluation of condition. Generally used to update or increment the counter variable.
In your case you are not assigning any value to i. You should replace it with something like this:
function addTwo() {
for (i=0; i<=100; i+=2) {
console.log(i);
}
}
addTwo();
i++ is a short hand for i += 1 which is called Increment Operator But i+2 or even i+1 will not increase the value of i. You need to increase it by assigning a new value to i. i = i + 2 or i += 2.
Number is one of the primitive types in javascript which mean you can't change it unless you use assignment operator =
Note: You are not using let or var with i this will make i a global variable.
function addTwo() {
for (let i = 0; i <= 100; i+=2) {
console.log(i);
}
}
addTwo();
for (i = 0; i <= 20; i++) {
console.log(i);
i++;
}
You can increase i twice Or
for (i=0; i<=100; i+=2) {
console.log(i);
}
you can use i+=2 it will increase value of i 2 times and set new value of i.

javascript for-loop and timeout function

I am facing an issue with a for-loop running a setTimeout.
for (var x = 0; x < 5; x++) {
var timeoutFunction = function() {
return function() {
console.log(x)
}
}
setTimeout(timeoutFunction(), 1)
}
I expect the output
0
1
3
4
Yet, for some reason they all output 5.
Variable x is defined in the local scope of the for-loop, so I thought this may not count for the callback of setTimeout. I tested with defining x outside of the for-loop.
var x = 10
for (var x = 0; x < 5; x++) {
var timeoutFunction = function() {
return function() {
console.log(x)
}
}
setTimeout(timeoutFunction(), 1)
}
I figured this output would be giving 10, yet it didn't. Then I thought it would make sense to define x afterwards.
for (var x = 0; x < 5; x++) {
var timeoutFunction = function() {
return function() {
console.log(x)
}
}
setTimeout(timeoutFunction(), 1)
}
var x = 10
This does return only 10. This implies the callbacks are all called after the for-loop is executed? And why do they only conform to the parent scope of the for-loop once the variable is initialised after execution of the for-loop? Am I missing something?
I know how make this example working with
for (var x = 0; x < 5; x++) {
var timeoutFunction = function(x) {
return function() {
console.log(x)
}
}
setTimeout(timeoutFunction(x), 1)
}
Yet, I really wonder what is missing...
Note that specifying 1 as the delay value will not actually cause the function to execute after 1 millisecond. The fastest the function could execute is roughly 9 milliseconds (and that's based on the internals of the browser), but in reality, it can't run the function until there is no other code running.
As for the unexpected output, you are experiencing this behavior because the timeoutFunction includes a reference to the x variable that is declared in a higher scope. This causes a closure to be created around the x variable, such that each time the function runs it does not get a copy of x for itself, but it is sharing the x value because it is declared in a higher scope. This is the classic side effect of closures.
There are a few ways to adjust the syntax to fix the issue...
Make a copy of x and let each function use its own copy by passing x into the function. When you pass a primitive type (boolean, number, string) a copy of the data is created and that's what is passed. This breaks the dependence on the shared x scope. Your last example does this:
for (var x = 0; x < 5; x++) {
var timeoutFunction = function(x) {
return function() {
console.log(x)
}
}
// Because you are passing x into the function, a copy of x will be made for the
// function that is returned, that copy will be independent of x and a copy will
// be made upon each loop iteration.
setTimeout(timeoutFunction(x), 1)
}
Another example that does the same thing would be needed if your timeout function wasn't returning another function (because there would be no function to pass the value to). So, this example creates an extra function:
for (var x = 0; x < 5; x++) {
// This time there is no nested function that will be returned,
function timeoutFunction(i) {
console.log(i);
}
// If we create an "Immediately Invoked Funtion Expression" (IIFE),
// we can have it pass a copy of x into the function it invokes, thus
// creating a copy that will be in a different scope than x.
(function(i){
setTimeout(function(){
timeoutFunction(i); // i is now a copy of x
}, 1);
}(x));
}
If you are working with browsers that support the ECMAScript 2015 standard, you can simply change the var x declaration in your loop to let x, so that x gets block level scope upon each iteration of the loop:
// Declaring a variable with let causes the variable to have "block-level"
// scope. In this case the block is the loop's contents and for each iteration of the
// loop. Upon each iteration, a new "x" will be created, so a different scope from
// the old "x" is what's used.
for (let x = 0; x < 5; x++) {
var timeoutFunction = function() {
return function() {
console.log(x)
}
}
setTimeout(timeoutFunction(), 1)
}
You have to create new scope for your function to make it 'remember' value from given iteration:
for (var x = 0; x < 5; x++) {
var timeoutFunction = (function(x) {
return function() {
console.log(x)
}
})(x)
setTimeout(timeoutFunction(), 1)
}
Another solution is to use ES2015 let:
for (let x = 0; x < 5; x++) {
var timeoutFunction = function() {
console.log(x)
}
setTimeout(timeoutFunction(), 1)
}
use this snippet
for(let x = 0; x < 5; x++) {
var timeoutFunction = function() {
console.log(x);
};
setTimeout(timeoutFunction, 1);
};
console.log('For loop completed');
JavaScript is not multi threaded so with your code the timeoutFunction will execute after the for loop is completed since you are using the global variable (with respect to timeoutFunction context) the final result is printed

How do i maintain the value of the loop variable in a setTimeout? Javascript [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 7 years ago.
Basically, i have a loop where if a certain condition is met, it fires a setTimeout.
But the timeout function requires the value of the loop ( i.e 'i'), as example below.
How do i do that?
for( var i = 0; i <= 10; i++) {
if(something){
setTimeout(function(){
console.log(i);
}, 1000);
}
}
Use IIFE (immediately-invoked function expression), it is a JavaScript design pattern which produces a lexical scope using JavaScript's function scoping.
for( var i = 0; i <= 10; i++) {
if(something){
(function(i){
setTimeout(function(){
console.log(i);
}, 1000);
})(i);
}
}
for (var i = 0; i <= 10; i++) {
if (true) {
setTimeout((function(i) {
return function() { console.log(i); }
})(i), 1000 * i);
}
}
and I think you want the time to be 1000 * i ? (It's already in the code above)

javascript closure in loop

Following code is given:
var a = [ ], i = 0, j = 0;
for (i = 0; i < 5; i += 1) {
(function(c) {
a.push(function () {
console.log(c); });
})(i);
};
for (j = 0; j < 5; j += 1) { a[j](); }
Why does i always get bigger by 1 instead of staying 5? Hasn't the foor loop already been passed, so the i parameter given to the anonymous function should be 5?
If you referenced i from the inner closure then yes, you would see the result being 5 in all cases. However, you pass i by value to the outer function, which is accepted as parameter c. The value of c is then fixed to whatever i was at the moment you created the inner closure.
Consider changing the log statement:
console.log("c:" + c + " i:" + i);
You should see c going from 0 to 4 (inclusive) and i being 5 in all cases.
chhowie's answer is absolutely right (and I upvoted it), but I wanted to show you one more thing to help understand it. Your inner function works similarly to a more explicit function call like this:
var a = [ ], i = 0, j = 0;
function pushFunc(array, c) {
array.push(function () {
console.log(c);
});
}
for (i = 0; i < 5; i += 1) {
pushFunc(array, i);
}
for (j = 0; j < 5; j += 1) { a[j](); }
Which should also help you understand how c comes from the function argument, not from the for loop. Your inner function is doing exactly the same thing as this, just without an externally declared named function.

Breaking out of nested loops: return or label/break?

I am using a JavaScript function to set a global variable. Below, I have two really dumb example functions. One uses a label to break out of the nested loops. The other uses an empty return.
My question: which is better from a performance issue? (For the sake of argument, lets say you did this a few million times.)
Using empty return
function foo() {
for(var i = 0; i < 100; ++i) {
for(var j = 0; j < 100; ++j) {
if(i * j == 50) {
myGlobal = j;
return;
}
}
}
}
Using label and break
function foo() {
dance:
for(var i = 0; i < 100; ++i) {
for(var j = 0; j < 100; ++j) {
if(i * j == 50) {
myGlobal = j;
break dance;
}
}
}
}
I know that I will be doing nothing except finishing the function after my inner condition is met/I make my assignment.
Thanks!
After some testing (via Chrome console, MBP 2013, OSX 10.9, Intel i7 # 2.8GHz, 16GB DDR3), the results are very interesting. I ran two types of tests. The first tested using return and label/break to break out of a nested loop. The second used a straight return and label/break, with nothing else in the function. The test code:
function r() {
for(var i = 0; i < 10; ++i) {
for(var j = 0; j < 10; ++j) {
if(i*j == 50) {
return;
}
}
}
}
function b() {
dance:
for(var i = 0; i < 10; ++i) {
for(var j = 0; j < 10; ++j) {
if(i*j == 50) {
break dance;
}
}
}
}
function r2() {
return;
}
function b2() {
dance:
break dance;
}
var startTime;
var endTime;
console.log("Return test");
startTime = Date.now();
for(var i = 0; i < 1000000000; ++i) {
r2();
}
endTime = Date.now();
console.log(endTime - startTime);
console.log("Break test");
startTime = Date.now();
for(var i = 0; i < 1000000000; ++i) {
b2();
}
endTime = Date.now();
console.log(endTime - startTime);
When comparing breaking out of a the nested loops (functions r() and b() ), the return consistently performed significantly better. However, when using just the return or label/break in the function (functions r2() and b2() ) the label/break performed significantly faster. Test result breakdown:
Test 1, using 10000000 iterations
Average runtime (milliseconds) after 3 runs of using return to leave nested loops: 1215ms
Average runtime (milliseconds) after 3 runs of using label/break to leave nested loops: 1522ms
Test 2, using 1000000000 iterations //2 orders of magnitude more iterations
Average runtime (milliseconds) after 3 runs of using return: 1879ms
Average runtime (milliseconds) after 3 runs of using label/break: 1862ms
Thus:
For breaking nested loops, using return is ~25% faster
For science/HPC, using label/break is ~1% faster
I personally don't see anything wrong with empty return statements to abort execution early for a function that doesn't return anything normally. It's certainly cleaner than a label, and it's more language-agnostic too. A lot of languages don't support labeled for loops like whatever language your example is in, so the empty return statement will be simpler to understand for people coming from other languages lacking that feature.
Both have the same performance; the former arguably is more readable.
However, the latter makes it easier to modify the function should you need to add more instructions in the future after the loops.
UPDATE:
Good info here: Why JavaScript functions always return a value?, first answer says: 'Thus, return and function-executed-to-its-end semantics match.' So even if you use break, at the end of the execution it will return the same as if you use return

Categories

Resources