I have a code sample
for(var i = 0; i < 3; i++) {
setTimeout() {
console.log("i " + i)
}
}
This is printing "3" to the console three times. However, I want it to print "0, 1, 2" without using let.
So far, I have tried the following -
var funcs = [];
function createfunc(i) {
return function() {
console.log("i ", i)
}
}
for(var i = 0; i < 3; i++) {
setTimeout(() => {
funcs[i] = createfunc(i) // statement 1
}, 1000)
}
for (var j = 0; j < 3; j++) {
funcs[j]();
}
This is working fine if I use the statement 1 just as it is without putting it into the setTimeout function. However, it is throwing an error if I'm using the setTimeout function. Can someone let me know how can I make print 0, 1, 2 to the console using the setTimeout function ?
It happens because of closure, anyway you can use the below codes, then you will be getting the proper result that you want.
for(var i = 0; i < 3; i++) {
((i) => {
setTimeout(() => {
console.log("i " + i)
}, 0);
})(i);
}
Related
In the following scenario, event setTimeout get queued and only after stack clears out, i = 3 will print 3 times
//ver1
for (var i = 0; i < 3; i ++ ) setTimeout(()=> console.log(i), 3)
The following will print 1 2 3 in order since i is captured in func x ??
//ver2
for (var i = 0; i < 3; i++) {
(function x(i) {
setTimeout(() => {console.log(i)}, 30)
})(i);
}
Why the following only prints 2 one time?
//ver3
for (var i = 0; i < 3; i++) {
function x(i) {
setTimeout(() => {console.log(i)}, 30)
}(i);
}
I'm mostly confused between ver2 and ver3
EDIT:
ver2 explanation can be found here. Any insight on why ver3 only prints 2?
Your version 3 doesn't do at all what you think it does. Parenthesis are not optional on an IIFE. But because you named the function, it does get parsed as a function declaration:
for (var i = 0; i < 3; i++) {
function x(i) {
setTimeout(() => {console.log(i)}, 30)
}
(i);
}
You were never calling x(…) here. The (i) is just an unnecessarily-parenthesised expression evaluating the i variable. Your code executes the same as
for (var i = 0; i < 3; i++) {
i;
}
So why does this log 2 in the console? Because when you run this on the console, it prints the result value of the statement that was evaluated last: i, which had the value 2 in the last iteration of the loop.
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 4 years ago.
Here is a piece of code :
// -------- Sample 1 ---------------
var funcs = [];
for (var i = 0; i < 3; i++) {
funcs[i] = function() {
console.log("My value: " + i);
};
}
for (var j = 0; j < 3; j++) {
funcs[j]();
}
// --------- Sample 2 --------------
var fun = [];
for(var i = 0;i < 3; i++) {
fun[i] = function() {
console.log('values is '+ i);
}
}
for(var i = 0; i < 3; i++) {
fun[i]();
}
Both the sample codes are same except the variable names. But it's giving different output. The output of the above code is :
My value: 3
My value: 3
My value: 3
values is 0
values is 1
values is 2
Can any one figure out why its happening ?
DEMO
var is hoisted, so to the interpreter, the first part's i actually looks more like this:
var funcs = [];
var i;
for (i = 0; i < 3; i++) {
funcs[i] = function() {
console.log("My value: " + i);
};
}
// At this point, the for loop has finished; `i` has the value of 3 now:
for (var j = 0; j < 3; j++) {
// Each function retrieves the value of i and displays it
// and since `i` is 3, each function displays 3:
funcs[j]();
}
In your second sample (which is somewhat invalid JS because i gets declared twice), i gets reassigned to values 0, 1, and 2 while looping over the function that displays i.
I have the following code:
for(var i = 0; i <=3; i++){
setTimeout(function(){
var j = i;
function(){
console.log(j);
}
}, 1000);
}
I am getting an error 'Function name expected' on the second function. I don't understand why I can't use an anonymous function here.
To create a closure and call it right away do this:
(function(){
console.log(j);
}());
But, to actually capture the i correctly for the inner function, you can move the function up a bit and pass it i:
for (var i = 0; i <= 3; i++) {
(function(i){
setTimeout(function(){
console.log(i);
}, 1000);}(i));
}
And, to actually print the numbers 1 second after each other, you can do this:
for (var i = 0; i <= 3; i++) {
(function(i){
setTimeout(function(){
console.log(i);
}, 1000*(i+1));}(i));
}
Based on Jordão's answer, I ended up using the following code:
for(var i = 0; i <=3; i++){
(function(){
var j = i;
setTimeout(function(){
console.log(j);
}, 1000);
}());
}
So you know for the future you can get your output with a smaller (and cleaner?) number of lines of code if you use recursion:
function loop(i) {
console.log(i = i || 0);
if (i <= 3) setTimeout(loop, 1000, ++i);
}
loop();
DEMO
Is there any way to assign a variable as the conditional operator in a for loop in Javascript?
For example, take this for loop:
for(var i = 0, j = 5; i < 10; i++, j++) {
console.log(i + ", " + j);
}
I want to be able to set the i < 10 part as a variable. I might want the for loop to run until j < 8 instead of i < 10. The way I am currently doing it is using an if statement and then using two different for loops with the exact same code except for the conditional, and I just wanted to know if a more efficient way was possible. Thanks!
Use a function in the for loop and reassign it to whatever you want.
var predicate = function(i, j) { return i < 10; }
for(var i = 0, j = 5; predicate(i, j); i++, j++) {
console.log(i + ", " + j);
if (i === 5) { // Dummy condition
predicate = function(i, j) { return j < 8; };
}
}
The advantage is that you can completely change the logic if you need to.
You can replace the 10 with a variable. You cannot replace the operator (<) with a variable.
You can replace the entire expression with a function though, and that function can be stored in a variable.
var data = ["a", "b", "c"];
function loop(callback) {
for (var i = 0; callback(i); i++) {
console.log(data[i]);
}
}
loop(function (i) {
return (i < 10);
});
loop(function (i) {
return (i < 8);
});
Sure, did you try just sticking variables in there :
var total = true==true ? 8 : 10,
i = 0,
j = 5,
what = true==false ? i : j;
for(; what < total; i++, j++) {
console.log(i + ", " + j);
}
I guess you can create a random function for this?
function countdown = (function(){
var i =0, j = 5;
return function(){
++i; ++j;
return i < 10; // or j < 8
}
})();
while(countdown()) {
console.log('counting');
}
The only problem with that is that you cannot access i and j in the scope of your loop.
I'm not sure why I'm getting an error on the following snippet (adapted from JavaScript closure inside loops – simple practical example):
var funcs = {};
for (var i = 0; i < 3; i++) { // let's create 3 functions
funcs[i] = (function(n) { // and store them in funcs
console.log("My value: " + n); // each should log its value.
})(i);
}
for (var j = 0; j < 3; j++) {
funcs[j](); // and now let's run each one to see
}
It seems like this should run ok; I know this is just something I don't fully get.
Here is the errror I get:
thx for any help
You need to return a function, rather than a result of the function. Try:
funcs[i] = (function(n) {
return function() {
console.log("My value: " + n);
}
})(i);
Example:
> var funcs = {};
for (var i = 0; i < 3; i++) {
funcs[i] = (function(n) {
return function() {console.log("My value: " + n);}
})(i);
}
for (var j = 0; j < 3; j++) {
funcs[j]();
}
My value: 0
My value: 1
My value: 2