Block scoping in JavaScript for loops? - javascript

Why does this code print 6 with VAR but "i" and the incremented for loop value with LET?
for (var i = 1; i <= 5; i++) {
setTimeout(function () {
console.log("i " + i);
}, i * 1000)
}

Variables defined as var is automatically moved to the top.
Even if you define it like this:
for (var i = 1; i <= 5; i++) {
setTimeout(function () {
console.log("i " + i);
}, i * 1000)
}
it is used as if you had written
var i;
for (i = 1; i <= 5; i++) {
setTimeout(function () {
console.log("i " + i);
}, i * 1000)
}
Since i is now like a global variable, the for-loop executes and leaves the variable with value 6. When the function is called by setTimeout in the future, the value is 6 for all calls.
You can read more about let and var.

The difference between var and let is the scope:
var scope:
Declared variables are constrained in the execution context in which they are declared. Undeclared variables are always global.
let scope:
Variables declared by let have as their scope the block in which they are defined, as well as in any contained sub-blocks.
When you run your example with var, the loop will execute quickly and end before the setTimeout() functions starts executing, and when they execute, they get the current value of the i variable which is 6.
When you run your example with let, the loop will also execute quickly and end before the setTimeout() functions starts executing. However this time because the i variable is scoped to the loop, it will keep each value in the context memory for each setTimeout() function call.
To achieve the same effect with 'var', you can create a different context using a closure to preserve the value, like what let does:
for (var i = 1; i <= 5; i++) {
(function(i) {
setTimeout(function() {
console.log("i " + i);
}, i * 1000);
})(i);
}

Related

Unable to figure out the output of simple javascript code

function func2() {
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 2000);
}
}
func2();
Due to closure, the console.log statement should remember its outer lexical environment, which is first setTimeout and then the for loop. Since i is not available in the setTimeout context, it looks in the execution context of the for loop, and searches for the value of i there.
Now, that value, after 2 seconds, should have been 3 for all three executions of console.log. But it displays the output as:
0
1
2
You have used let which is block scoped so there will be separate i for each for loop iteration.
1) If you want to output consective 3 so you can declare i outside of for-loop, then there will be only single i with the value 3.
function func2() {
let i;
for (i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 2000);
}
}
func2();
2) If you want same lexical scope then you can also use var here,
var is function scoped, So there will only be one i
function func2() {
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 2000);
}
}
func2();

setTimeout in javascript does not show updated value

why am I getting the outpupt 10 instead of 20. Why the setTimeout is not taking the latest value?
var a=10;
function foo(myvar){
console.log(myvar);
}
setTimeout(foo,1000,a);
a=20;
if I put setTimeout in a loop then it consoles the latest value
for (var i = 0; i < 3; i++) {
setTimeout(function log() {
console.log(i);
}, 1000);
}
3 3 3
This is because your setTimeout thread was initialized when the value for variable a is 10. There after you updated the value of variable a. But the value of parameter is still the old value 10.
var a = 10;
function foo(myvar) {
console.log('Value for param ', myvar);
console.log('Value for a ', a );
console.log('Thread Executed');
}
console.log('Thread Started');
setTimeout(foo, 1000, a);
a = 20;
console.log('Variable Updated');
What is happening in the other case was you are logging the value of the index i which is in the for loop. At the time of execution of console, the value of i will be the last value, which is 3
One issue is - you're not comparing like for like code
var a=10;
function foo(myvar){
console.log(myvar);
}
setTimeout(foo,10,a);
a=20;
is comparable to
var i;
function log(myvar) {
console.log(myvar);
}
for (i = 0; i < 3; i++) {
setTimeout(log, 10, i);
}
Whereas
for (var i = 0; i < 3; i++) {
setTimeout(function log() {
console.log(i);
}, 10);
}
Is exactly equivalent to
var i;
function log() {
console.log(i);
}
for (i = 0; i < 3; i++) {
setTimeout(log, 10);
}
and comparable to
var a=10;
function foo(){
console.log(a);
}
setTimeout(foo,10);
a=20;
You are passing a by value in the call to setTimeout(). Therefore no matter how many times you change the value of a after calling setTimeout() you'd not get any of the new values.
The following snippet would work:
var a=10;
function foo(){
console.log(a);
}
setTimeout(foo,1000);
a=20;
Because a is captured as part of the closure(foo())'s calling context. Notice that a is not passed to setTimeout() explicitly.
The second example you gave appears to work to you but it is actually exactly the same as the original sample it's just that you pass three different values - 1, 2, and 3 - to three consecutive calls to setTimeout(). If you added an if-statement inside the loop to ensure that you only call setTimeout() when i == 1 you'd see exactly the same behavior as in your first snippet.

keep for loop from executing in Javascript

Currently for loop gets executed till the end even though the function it calls hasn't finished executing. I want to make it such that, when startloop function is called until it is executed completely for loop shouldn't move forward.. How to do that? I tried simulating goto but it didn't work..
Here's my code:
function startLoop(i) {
console.log("startloop function start");
var centerX = xObj[i];
var centerY = yObj[i];
var radius = 10;
var alpha = 1, /// current alpha value
delta = 0.01; /// delta = speed
var flag = 0;
var num = 0
function loop() {
console.log("inside loop " + centerX + " " + centerY);
alpha -= delta;
if (alpha <= 0) {
//console.log("heya_amigoes");
num = 2;
return;
}
//console.log("hi1");
/// clear canvas, set alpha and re-draw image
ctx2.clearRect(0, 0, 1000, 600);
ctx2.globalAlpha = alpha;
ctx2.beginPath();
ctx2.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
ctx2.fillStyle = "#FF0000";
ctx2.fill();
ctx2.lineWidth = 1;
ctx2.strokeStyle = '#003300';
ctx2.stroke();
//console.log("hi2");
//requestAnimationFrame(loop); // or use setTimeout(loop, 16) in older browsers
setTimeout(loop, 16)
console.log("Outside function loop");
}
loop();
/*
LABEL1: do {
if(num==2)
{
num=0;
break LABEL1;
}
if(num!=2)
continue LABEL1;
}while(1);
*/
console.log("startloop function stop");
}
for (i = 0; i < xObj.length; i++) {
console.log("for loop running " + i);
startLoop(i);
console.log("outside loop func");
}
A for loop will not wait for your task. To achieve this task, you will have to use recursion.
Logic:
Call a function and pass a callback in it.
On execution completion, run passed callback.
Now since we have to chain same function again and again, pass same callback to next iteration again and have a check(threshold limit) and stop on breach.
var count = 0
function test1(i, cb){
console.log("In Test " + i)
if(i < 10)
setTimeout(cb.bind(null, ++count, cb), 2000)
}
test1(count, test1)
Explanation:
My approach mimics behaviour of a do..while loop. It has 4 parts:
Initialisation: This will initialise the iterator variable. This variable will be use to check for termination condition and to check the current iterator's value. In my Case: count = 0
Execution of Code block: This will execute the code defined inside {...}. In my case: test1(count, test1)
Termination condition: This check if next iteration should be performed or not? In my case: if(i<10). If condition is satisfied, start next iteration: setTimeout(cb.bind(null, ++count, cb), 2000)
Increment Value: This updated value of iterator to point to next value. In my case: ++count
This is the final JSFiddle
I think that the problem going to be with your for loop. startLoop function always going to get the xObj.length-1.
You can read more about this problem: JavaScript closure inside loops – simple practical example
The variable i, within each of your anonymous functions, is bound to
the same variable outside of the function.
Solution with ES6 let feature:
for (let i = 0; i < xObj.length; i++) {
console.log("for loop running " + i);
startLoop(i);
console.log("outside loop func");
}
Solution without ES6:
var funcs = [];
for (var i = 0; i < xObj.length; i++) {
funcs[i] = startLoop(i);
}
for (var i = 0; i < xObj.length; i++) {
console.log("for loop running " + i);
funcs[i]();
console.log("outside loop func");
}

Delay javascript code execution inside for loop

I have the following for loop:
for (var i = tileLog.length - 1; i >= 0; i--) {
$('.' + tileLog[i]).mouseenter();
};
1 < tileLog.legth < 1025
Is there a way to delay each iteration of the loop so that mouseenter() is triggered every x miliseconds?
I have tried:
function doSetTimeout(i) {
setTimeout(function() { $('.' + i).mouseenter(); }, 250);
}
for (var i = tileLog.length - 1; i >= 0; i--)
doSetTimeout(tileLog[i]);
This doesn't seem to work, it just delays by 250ms then iterates through the loop
As an alternative to using setTimeout() you could also use setInterval().
Define a running variable in the outer scope (like your running i in the loop).
In each iteration, besides calling your function, decrement the running variable. If it is below zero, stop the setInterval()`` :
var index = tileLog.length - 1,
timer = setInterval( function(){
$('.' + tileLog[index]).mouseenter();
index -= 1;
if ( index < 0 ) {
clearInterval( timer );
}
}, 250 );
There is no actual sleep() function or something similar. Would also be problematic as JavaScript (for most cases) is single threaded and such a method would block the render thread, thus rendering your browser inaccessible.
There is no sleep or such in JavaScript. So your approach with timeout is correct.
var tileLog;
var i = titleLog.length - 1;
function func1() {
$('.' + tileLog[i]).mouseenter();
if (--i) {
window.setTimeout(func1, 250);
}
}
// and of course start the process
func1();

Looping setTimeout

I'm currently trying to wrap my head around some JavaScript.
What I want is a text to be printed on the screen followed by a count to a given number, like so:
"Test"
[1 sec. pause]
"1"
[1 sec. pause]
"2"
[1 sec. pause]
"3"
This is my JS:
$(document).ready(function() {
var initMessage = "Test";
var numberCount = 4;
function count(){
writeNumber = $("#target");
setTimeout(function(){
writeNumber.html(initMessage);
},1000);
for (var i=1; i < numberCount; i++) {
setTimeout(function(){
writeNumber.html(i.toString());
},1000+1000*i)};
};
count();
});
This is my markup:
<span id="target"></span>
When I render the page, all I get is "Test" followed by "4".
I'm no JavaScript genius, so the solution could be fairly easy. Any hints on what is wrong is highly appreciated.
You can play around with my example here: http://jsfiddle.net/JSe3H/1/
You have a variable scope problem. The counter (i) inside the loop is only scoped to the count function. By the time the loop has finished executing, is value is 4. This affects every setTimeout function, which is why you only ever see "4".
I would rewrite it like this:
function createTimer(number, writeNumber) {
setTimeout(function() {
writeNumber.html(number.toString());
}, 1000 + 1000 * number)
}
function count(initMessage, numberCount) {
var writeNumber = $("#target");
setTimeout(function() {
writeNumber.html(initMessage);
}, 1000);
for (var i = 1; i < numberCount; i++) {
createTimer(i, writeNumber);
}
}
$(document).ready(function() {
var initMessage = "Test";
var numberCount = 4;
count(initMessage, numberCount);
});
The createTimer function ensures that the variable inside the loop is "captured" with the new scope that createTimer provides.
Updated Example: http://jsfiddle.net/3wZEG/
Also check out these related questions:
What's going on under the hood here? Javascript timer within a loop
JavaScript closure inside loops – simple practical example
In your example, you're saying "2, 3, 4 and 5 seconds from now, respectively, write the value of i". Your for-loop will have passed all iterations, and set the value of i to 4, long before the first two seconds have passed.
You need to create a closure in which the value of what you're trying to write is preserved. Something like this:
for(var i = 1; i < numberCount; i++) {
setTimeout((function(x) {
return function() {
writeNumber.html(x.toString());
}
})(i),1000+1000*i)};
}
Another method entirely would be something like this:
var i = 0;
var numberCount = 4;
// repeat this every 1000 ms
var counter = window.setInterval(function() {
writeNumber.html( (++i).toString() );
// when i = 4, stop repeating
if(i == numberCount)
window.clearInterval(counter);
}, 1000);
Hope this helps:
var c=0;
var t;
var timer_is_on=0;
function timedCount()
{
document.getElementById('target').value=c;
c=c+1;
t=setTimeout("timedCount()",1000);
}
function doTimer()
{
if (!timer_is_on)
{
timer_is_on=1;
timedCount();
}
}

Categories

Resources