Define function based on global variable in javascript [duplicate] - javascript

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 7 years ago.
I'm trying to print out a number every 5 seconds:
for(i=0; i<10; i++) {
setTimeout(function() {
console.log(i)
}, 5000 * i);
}
But instead I print 10 every 5 seconds because the loop finished and the global variable i is ten. Is there a way to avoid this happening?

You can use bind to create a closure:
for (i = 0; i < 10; i++) {
setTimeout((function(num) {
console.log(num);
}).bind(this, i), 5000 * i);
}

If ES6 Is allowed you could use a let declaration.
for (let i = 0; i < 10; i++) {
setTimeout(function () {
console.log(i)
}, 5000 * i);
}

The setTimeout function in javascript doesn't work like time.sleep(n) in python. The code will not pause and continue to run after 5s. It will set the body of the function in a queue that will be served in the time you picked. Since you loop 10 times, it will set all of your orders which are basically print i in a queue and serve them all together. To avoid this you can make a use of your loop in something that looks quite like this
setTimeout(function(){ console.log(i)}, 5000*i);
which will schedule each if the print 5s away from the last one !

Related

I can't understand how this program is being executed [duplicate]

This question already has answers here:
setTimeout in for-loop does not print consecutive values [duplicate]
(10 answers)
Closed 6 years ago.
This is a javascript code snippet. I don't understand how only '5' is being printed at the output. That too only once.
Here is the code :
for(var i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, 1000 * i);
}
output - 5.
Shouldn't 1,2,3,4 be the output?
First of all it doesn't print 5 once but 5 times each at an interval of 1 sec
for(var i=0; i<5 ; i++) {
setTimeout( function() {
console.log(i) ; }, 1000*i);
}
It happens because the timeout function callbacks are all running well after the completion of the loop. In fact, as timers go, even if it was setTimeout(.., 0) on each iteration, all those function callbacks would still run strictly after the completion of the loop, that's why 5 was reflected as that will be the last value of i in the loop. Each of the timer handler functions will share the same variable "i"
If you want to seperate value each time. you can call a separate function in it passing i as a value and hence each will have a different value
function checkTimeout(i) {
setTimeout(function() { console.log(i); }, 1000*i);
}
for (var i = 1; i <= 5; ++i) {
checkTimeout(i);
}

Passing array and index within `for` loop and `setTimeout()` [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 7 years ago.
I would like to execute a click on templates[i] within setTimeout within a for loop
for(var i=0; i<templates.length; i++){
setTimeout(function(){
(function(){
templates[i].click();
}(i, templates));
}, 200);
}
I get the error templates[i] is undefined.
However, something like this works fine:
for(var i=0; i<templates.length; i++){
setTimeout(function(){
(function(){
console.log(templates_arr+templates)
}(templates_arr, templates));
}, 200);
}
Can anybody shed some light onto why this is like this and how I can pass the array and index properly?
Thanks,
Dan
it should be
for(var i=0; i<templates.length; i++){
(function(i,templates){
setTimeout(function(){
templates[i].click();
}, 200);
})(i, templates);
}

Why 'i' passed to a function inside a for loop is equal to a number greater than 'i'? [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 7 years ago.
I have this for loop:
for (var i = 1; i < array.length; i++) {
$('#shop'+i).on('click', function(){ buyThis(i) });
}
The problem is that the i passed as argument to buyThis() isn't equal to the i.
array.length is equal to 3 and #shop1 and #shop2 are created but the argument passed to buyThis() is 3. Why is that?
I'm trying to get an event in which clicking on #shop1 triggers buyThis(1) and clicking on #shop2 triggers buyThis(2).
That's because the code inside the function is called later, when the loop has finished and the variable i has reached its final value.
You can wrap the code that sets the event in an immediately invoked function expression (IIFE) to create a variable for each iteration of the loop:
for (var i = 1; i < array.length; i++) {
(function(i){
$('#shop' + i).on('click', function(){ buyThis(i); });
})(i);
}
This is a scope issue. You need to create a proper closure
for (var i = 1; i < array.length; i++) {
(function(index){
$('#shop'+index).on('click', function(){ buyThis(index) });
})(i);
}
The reason it was different before is because in javascript. the variable is still visible to the handler and by the time it is accessed, it is set to the latest iteration.
This is an issue with block scoping, you can use a closure to solve this
for (var i = 1; i < array.length; i++) {
(function(index){
$('#shop'+index).on('click', function(){ buyThis(index) });
})(i);
}

setTimeout() - in for loop with random delay [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Javascript closure inside loops - simple practical example
Seen many posts talking about setTimeout and closures but I'm still not able to pass in a simple for loop counter.
for (i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, Math.floor(Math.random() * 1000));
}
Gives
5
5
5
5
5
Would like to have
0
1
2
3
4
What's wrong ?
Please don't flame, I thought I have understood the setTimeout() tale but apparently not.
You can use a closure to keep a reference to the current value of i within the loop:
for (i = 0; i < 5; i++) {
(function(i) {
setTimeout(function () {
console.log(i);
}, Math.floor(Math.random() * 1000));
})(i); //Pass current value into self-executing anonymous function
}​
However, this is unlikely to print the numbers in order since you use a random timeout (you could use i * 1000 instead to make the numbers print in ascending order, one second apart).
Here's a working example.
You need to pass i to the function being used in the setTimeout. By the time your first method is executed, i is already set to 5.
Since your timeout is variable due to the call to Math.Random(), the timeouts will be different and you won't get them in the sequence you're expecting.
Here's a working example
for (i = 0; i < 5; i++) {
(function(i) {
setTimeout(function () {
console.log(i);
}, 1000);
})(i);
}
Changing Math.floor(Math.random() * 1000) to simply 1000 ensures that the functions execute in the order you're expecting.
You need to wrap the "interesting" code in a function that closes over i and copies it in a local variable:
for (i = 0; i < 5; i++) {
(function() {
var j = i;
setTimeout(function () {
console.log(j);
}, Math.floor(Math.random() * 1000));
})();
}
The "intermediate" function call forces the value of j to be fixed at the point that function is called, so within each setTimeout callback the value of j is different.

how restricted is recursion in javascript?

I guess its to stop browsers getting nailed all the time by duff code but this:
function print(item) {
document.getElementById('output').innerHTML =
document.getElementById('output').innerHTML
+ item + '<br />';
}
function recur(myInt) {
print(myInt);
if (int < 10) {
for (i = 0; i <= 1; i++) {
recur(myInt+1);
}
}
}
produces:
0
1
2
3
4
5
6
7
8
9
10
10
and not the big old mess I get when I do:
function recur(myInt) {
print(myInt);
if (int < 10) {
for (i = 0; i <= 1; i++) {
var x = myInt + 1;
setTimeout("recur("+x+")");
}
}
}
Am I missing something or is this how you do recursion in JS? I am interested in navigating trees using recursion where you need to call the method for each of the children.
You are using a global variable as loop counter, that's why it only loops completely for the innermost call. When you return from that call, the counter is already beyond the loop end for all the other loops.
If you make a local variable:
function recur(int) {
print(int);
if (int < 10) {
for (var i = 0; i <= 1; i++) {
recur(int + 1);
}
}
}
The output is the same number of items as when using a timeout. When you use the timeout, the global variable doesn't cause the same problem, because the recursive calls are queued up and executed later, when you have exited out of the loop.
I know what your doing wrong. Recursion in functions maintains a certain scope, so your iterator (i) is actually increasing in each scope every time the loop runs once.
function recur(int) {
print(int);
if (int < 10) {
for (var i = 0; i <= 1; i++) {
recur(int+1);
}
}
}
Note it is now 'var i = 0' this will stop your iterators from over-writing eachother. When you were setting a timeout, it was allowing the first loop to finish running before it ran the rest, it would also be running off the window object, which may remove the closure of the last iterator.
Recursion is very little restricted in JavaScript. Unless your trees are very deep, it should be fine. Most trees, even with millions of elements, are fairly wide, so you get at most log(n) recursive calls on the stack, which isn't noramally a problem. setTimeout is certainly not needed. As in your first example, you're right that sometimes you need a guard clause to guarantee that the recursion bottoms out.

Categories

Resources