Here is the code
function fn(){
for (var i = 0; i < 4; i++) {
var tc=setTimeout(function(i){
console.log(i)
clearTimeout(tc)
},10,i);
}
}
fn()
//0, 1, 2 - output
I can't understand why the output is not '0, 1, 2, 3',only output three times.
Even more stranger is below
When I change the loop times, now, I call loop times as T.
T: 1 ---> 0
T: 2 ---> 0
T: 3 ---> 0, 1
above, the right is the output.
Here you go. Print i out at interval T.
function fn(){
var T = 1000;
var func = function(i,len) {
if ( i < len ) {
console.log(i);
setTimeout(func,T,i+1,len); // Calling itself.
}
};
setTimeout(func,T,0,4);
}
fn();
At last got it...The problem is with closure..You just need to pass the variable tc to setTimeout function.
function fn(){
for (var i = 0; i < 4; i++) {
var tc=setTimeout(function(i,tc){
console.log(i)
clearTimeout(tc);
},10,i,tc);
}
}
fn()
What you are doing is that you are overwriting the value of tc in the for loop.
By overwriting, you lose handle to corresponding return value from each call to setTimeout().
You need to clear individual tc as you go in the for loop.
So, something like this would work.
Note that I have hard-coded, tc array but you can make it generic, so this example is just for a reference as to how you could get 0,1,2,3.
var tc = [0,0,0,0];
function fn(){
for (var i = 0; i < 4; i++) {
tc[i]=setTimeout(function(i){
console.log(i);
clearTimeout(tc[i]);
},10,i);
}
}
fn();
Related
Quoting from the book JavaScript: The Definitive Guide
Unlike variables declared with let, it is legal to declare the same variable multiple times with var. And because var variables have function scope instead of block scope, it is actually common to do this kind of redeclaration. The variable i is frequently used for integer values, and especially as the index variable of for loops. In a function with multiple for loops, it is typical for each one to begin for(var i = 0; .... Because var does not scope these variables to the loop body, each of these loops is (harmlessly) re-declaring and re-initializing the same variable.
I am unable to understand what is meant in this paragraph. First I assumed the following would not have worked with second loop not iterating, because i would be function scoped:
(function foo() {
for (let i = 0; i < 2; i++) {
console.log(i);
}
for (let i = 0; i < 2; i++) {
console.log(i);
}
})();
but it prints
0
1
0
1
Then I assumed this would have printed 0 0 1 1 0 1, which is also not the case:
(function foo() {
for (var i = 0; i < 2; i++) {
console.log(i);
for (var i = 0; i < 2; i++) {
console.log(i);
}
}
})();
Can someone help me understand what is meant by
in a function of multiple for loops, var can be used harmlessly in each loop
and how it is different to let?
To me it looks like the opposite it true (which is also very confusing) where let can be used harmlessly in functions with multiple loops:
(function foo() {
for (let i = 0; i < 2; i++) {
console.log(i);
for (let i = 0; i < 2; i++) {
console.log(i);
}
}
})();
prints:
0
0
1
1
0
1
let variables are forbidden to be re-declared in the same scope, like the following:
let foo = 10;
let foo = 20;
var variables can be, though:
var foo = 10;
var foo = 20;
console.log('ok');
In your first snippet, variables declared with let in the header of a for loop only exist inside the loop; they're block scoped, and aren't scoped to the outer block or function:
(function foo() {
for (let i = 0; i < 2; i++) {
// console.log(i);
}
for (let i = 0; i < 2; i++) {
// console.log(i);
}
// doesn't exist out here:
console.log(i);
})();
The let is above create is in two different scopes, which is why it's not forbidden. Similarly:
(function foo() {
{
let i = 10;
}
{
let i = 20;
}
console.log('OK up until here');
// doesn't exist out here:
console.log(i);
})();
vars, in contrast, have function scope, not block scope - with your var version of the loop, since there's only one function, this:
(function foo() {
for (var i = 0; i < 2; i++) {
console.log(i);
for (var i = 0; i < 2; i++) {
console.log(i);
}
}
})();
is equivalent to
(function foo() {
var i; // the `i` variable exists at this level
for (i = 0; i < 2; i++) {
console.log(i);
for (i = 0; i < 2; i++) {
console.log(i);
}
}
})();
Then I assumed this would have printed 0 0 1 1 0 1, which is also not the case:
i gets initialized to zero at the beginning of each loop.
The first loop begins once, at the start of the function.
The second loop begins just after logging i, on each iteration of the outer loop.
(function foo() {
for (var i = 0; i < 2; i++) {
console.log(i);
for (var i = 0; i < 2; i++) {
console.log(i);
}
}
})();
outer loop initialization: 0 is assigned to i
0 gets logged
inner loop initialization: 0 is assigned to i
0 gets logged
inner loop increments i to 1 and starts again
inner loop: 1 gets logged
inner loop increments i to 2 and breaks, since i < 2 is no longer fulfilled
outer loop increments i to 3 and breaks, since i < 2 is no longer fulfilled
So you get 0 0 1 logged.
What the article probably means by
in a function of multiple for loops, var can be used harmlessly in each loop
is that it can work if both loops are on the same level of the function, eg
for (...) {
}
for (...) {
}
It definitely won't work with nested loops, if both loops use the same variable, because there's only ever a single i variable, rather than one for each loop.
where let can be used harmlessly in functions with multiple loops:
For nested loops, yes, since variables declared with let are unique to each loop, rather than being shared across the whole containing function.
Wondering if there is by any chance to programmatically setting third statement of forloop
var conditionProgrammatically = 'i++';//or 'x--'
for (var i = 0; i < 10; conditionProgrammatically) {
console.log(i)
}
You can use any expression you want there including calling a function. You just need to be careful of scope. So, for example, this works:
var conditionProgramatically = () => i++ ;
for (var i = 0; i < 10; conditionProgramatically()) {
console.log(i)
}
But it depends on the fact that var i is in a scope shared by the function. This, however, doesn't work:
var conditionProgramatically = () => i++ ;
for (let i = 0; i < 10; conditionProgramatically()) {
console.log(i)
}
Because let is scoped to the block and not available.
Of course you can share an object which is mutable by passing it as an argument like:
fn = (o) => o.i += 1
for (let o = {i:0}; o.i < 10; fn(o)) {
console.log(o.i)
}
This allows you to use let, but is a little hard on the eyes.
All said, it's probably going to be easier to make your logic fit in a simple expression rather than calling a function. You can still perform some logic, though:
for (let i = 0; Math.abs(i) < 10; i = Math.random() > .65 ? i -1: i + 1) {
console.log(i)
}
You can set a variable and then operate with this variable according to your needs.
(remember that i-- is equivalent to i -= 1).
BTW, be careful because you would also have to change the condition, if not you will end up in an infinite loop. In your case, I would use abs()
var step = 1; // or var step = -1;
for (var i = 0; abs(i) < 10; i += step) {
console.log(i)
}
Usually, in functional programmings (like python and javascript), we can use dictionary (or objects) to store functions.
var myFunctions = {
"a": function (i) { return i + 1 },
"b": function (i) { return i - 3 }
};
Then, we can set the condition as the key to the dictionary:
myCondition = "a"; // this will set condition to increment by 1
Here is your for loop:
for (i = 0; i < n; i = myFunctions[myCondition](i)) {
// whatever
}
This question already has answers here:
Why let and var bindings behave differently using setTimeout function? [duplicate]
(2 answers)
Closed 5 years ago.
I understand that let has block scope and var has functional scope. But I do not understand in this case, how using let will solve the problem
const arr = [1,2,3,4];
for (var i = 0; i < arr.length; i++) {
setTimeout(function() {
console.log(arr[i])
}, 1000);
} // Prints undefined 5 times
const arr = [1,2,3,4];
for (let i = 0; i < arr.length; i++) {
setTimeout(function() {
console.log(arr[i])
}, 1000);
} // Prints all the values correctly
This is all related to the scope of the variable. Let's try to wrap both the pieces into functions, and observe the output:
function test() {
// `i` will be declared here, making it a non-for-loop scoped variable
const arr = [1, 2, 3, 4];
for (var i = 0; i < arr.length; i++) {
setTimeout(function() {
console.log(arr[i])
}, 1000);
} // Prints undefined 5 times
}
test();
So in the first case, i will be hoisted, and because of the asynchronous nature of setTimeout, i will immediately become 4 as the loop ends without waiting. This will make arr[i] to point to an undefined element in the array.
In the second case, i is not hoisted, and has scoped access to each iteration of the loop, making i accurately available to console.log statement. Thus the results are as per the expectations:
function test() {
const arr = [1, 2, 3, 4];
for (let i = 0; i < arr.length; i++) {
setTimeout(function() {
console.log(arr[i])
}, 1000);
} // Prints all the values correctly
}
test();
First of all, the output will be four times and not five times(as mentioned in your comment).
I pasted your code in Babel REPL and this is what I got,
"use strict";
var arr = [1, 2, 3, 4];
var _loop = function _loop(i) {
setTimeout(function () {
console.log(arr[i]);
}, 1000);
};
for (var i = 0; i < arr.length; i++) {
_loop(i);
}
Do you see how let works internally now? :-)
You can still use var for setTimeout. You can use an immediately-invoked function expression (IIFE) to create a closure around setTimeout such that the value of i is recognised by the setTimeout function.
const arr = [1,2,3,4];
for (var i = 0; i < arr.length; i++) {
(function(i){
setTimeout(function() {
console.log(arr[i])
}, 1000)})(i);
}
A common pitfall with JavaScript closures is running setTimeout() from a for loop, and expecting the counter to be passed with different values at each iteration, while in practice it gets assigned the last value before the setTimeout() functions execute:
for (i = 0; i < 10; i++) {
setTimeout(function () {
console.log(i)
}, 100);
} // => prints "10" 10 times
One solution to this is to have an Immediately Invoked Function Expression:
for (i = 0; i < 10; i++)
(function(j) {
setTimeout(function foo() {
console.log(j)
}, 100);
})(i); // prints 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
Another is to pass an extra callback argument to setTimeout() (which doesn't work in IE<9):
for (i = 0; i < 10; i++) {
setTimeout(function foo(n) {
console.log(n)
}, 100, i);
}
But why does the following, simplest, code, produce the same result (0, 1, 2, ... 9)?
for (var i = 0; i < 10; i++)
setTimeout(console.log(i), 100);
This apparently surprising behavior occurs because the first parameter to setTimeout can be a function as well as a string, the latter being eval()-ed as code.
So setTimeout(console.log(i), 100); will execute console.log(i) right away, which returns undefined. Then setTimeout("", 100) will be executed, with a NOP call after 100ms (or optimized away by the engine).
Just for grins, another thing you can do (when you've got .bind()) is
for (i = 0; i < 10; i++) {
setTimeout(function () {
var i = +this;
console.log(i)
}.bind(i), 100);
}
A little bit less of a mess than an IIFE.
why does the following, simplest, code, produce the same result (0, 1,
2, ... 9)?
for (var i = 0; i < 10; i++)
setTimeout(console.log(i), 100);
Because it actually doesn't. If you look closely, you will notice that the log messages will not need the tenth of a second before they appear in console. By calling the console.log(i) right away, you are only passing the result of the call (undefined) to setTimeout, which will do nothing later. In fact, the code is equivalent to
for (var i = 0; i < 10; i++) {
console.log(i);
setTimeout(undefined, 100);
}
You will notice the difference better if you replace the 100 by i*500 in all your snippets, so that the log messages should be delayed at an interval of a half second.
I was wondering how to pass a number to a function I created for a loop. For example, I have a loop that simply adds 1 to a value when it runs. How would I go about passing how many times I want the loop to run in a function? Like so:
var i = 0;
function blahBlah (i ?){
for (i=0,i>10(this is what I want to pass to the function),i++){
i++;
}
Then call the function:
blahBlah(number of times I want it to run);
I'm not sure i understand the question, but how about
function blahBlah(n) {
for(var i=0; i < n; i++) {
//do something
}
}
function blahBlah (noOfTimes){
for (var i=0 ;i < noOfTimes ;i++){
//i++; you already incremented i in for loop
console.log(i);//alert(i);
}
}
blahBlah(10);// call function with a loop that will iterate 10 times
You mean calling the function each iteration?
function blahBlah( i ) {
// do something with i
}
for ( var i = 0; i < 10; i++ ) {
blahBlah( i );
}
Maybe like this:
function runLoop(length) {
for (var i=0; i < length; i++) {
{loop actions}
}
}
First, you used , instead of ; in for loop.
Second, you need two variables here: the first one is how many times to repeat (i, the argument), the second is a counter (a, which iteration is it now)
function blah(i) {
for (var a=0; a<i; a++) {
doStuff();
}
}
Use a loop inside your function:
function BlahBlah(n) {
for (i=0; i < n; ++i) {
// do something...
}
}
or simply invoke the function in a for loop:
function Blahblah() { /* do something */ }
// elsewhere:
n = 42;
for (i=0; i < n; ++i) BlahBlah();