Consider this simple example:
var count = 0;
for (var i = 0; i < 4; i++ ) {
setTimeout(function() {
console.log(i, count++);
}, i * 200);
}
which outputs the following
4 0
4 1
4 2
4 3
I would guess that i always resolves to 4 because the setTimeout callback closes over the variable I but I can't figure out why the same doesn't hold true for count?
var count = 0;
for (var i = 0; i < 4; i++ ) {
setTimeout(function() {
console.log(i, count++);
}, i * 2000 );
}
The variable i is incremented by your for loop, and ends up with the value 4 before any of the timeout handlers run. The variable count, on the other hand, is only incremented inside the timeout handlers. When the first timeout handler fires, count will still be 0.
As the lads before me said, It's because the loop completes before the timeout is activated.
One way around this would be to put your timeout in a different function, and pass the i to it from within the loop. This ensures that for each iteration, the function is passed the correct i
This is also why count is set to the expected value; it lies outside of the loop's scope.
for (var i = 0; i < 4; i++) {
doSomething(i);
};
function doSomething(i) {
setTimeout(function() {
console.log(i);
}, i * 2000);
};
Related
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.
I'm studying closures but I don't understand why the second is always 1 instead of 2, 3, 4, 5 respectively. What I understand is here that since each function (even iife) has its own scope. Each i variable is captured its own stack actually that's why it allows changes of values of i though had been var used.
In the same way, setTimeout should catch each different i variables having 1,2,3,4,5 (seconds) respectively. But, it doesn't seem such as seen in the image. Could you help?
Maybe it is helpful that i is not a free variable for
setTimeout. (no idea it is of any relation)
Code
for (let i = 1; i <= 5; i++) {
(function (i) {
setTimeout(function () {
console.log(i)
}, i * 1000)
})(i)
}
I think the use of let and an IIFE is throwing you off. Using an IIFE creates a new scope for the function parameters. A less known fact is that using let in a loop creates a new scope on every iteration.
e.g.
Using var and an IIFE.
// 'i' is declared here once
for (var i = 1; i <= 5; i++) {
// 'i' is declared again and takes a new scope every time the function is executed
(function (i) {
setTimeout(function () {
console.log(i) // references the nearest-scoped 'i'
}, i * 1000)
})(i)
}
// => 1, 2, 3, 4, 5
Using var and no IIFE
// 'i' is declared here once
for (var i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i) // references nearest-scoped 'i'
}, i * 1000)
}
// => 6, 6, 6, 6, 6
Using let and no IIFE
// 'i' is given a new scope in every iteration
for (let i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i)
}, i * 1000)
}
// => 1, 2, 3, 4, 5
Using let and an IIFE. This is exactly your example.
// 'i' is given a new scope in every iteration
for (let i = 1; i <= 5; i++) {
// 'i' is declared again and takes a new scope every time the function is executed
(function (i) {
setTimeout(function () {
console.log(i) // references the nearest-scoped 'i'
}, i * 1000)
})(i)
}
// => 1, 2, 3, 4, 5
I don't understand too how the engine works, albeit i gives results as expected. I wouldn't expect persistently 1 second elapsing.
for (let i = 1; i <= 5; i++) {
(function (i) {
setTimeout(function () {
console.log(i)
}, (console.log(i),i * 1000))
})(i)
}
In a comment you've said:
I expect that 1 second later 1, 2 secs later 2, 3 secs later 3, 4 secs later 4, 5 secs later 5. But 1,2,3,4,5 are printed 1 second elapsed each time as in the image
That would be what would happen if you started the timer for 2 after the callback for timer 1 was called, but that's not what the code does. The code sets up the five timers all right away — a timer to show 1 after one second, a timer to show 2 after two seconds, a timer to show 3 after three seconds, etc. Since they all start at the same time, you see 1 after one second, 2 a second after that (two seconds after it was scheduled), 3 a second after that (three seconds after it was scheduled), etc.
To see that, let's show when we start each timer, and also how long it's been since the previous one fired. First, though, you don't need your IIFE, because you're using let in a for statement, so each loop iteration gets its own i variable. So here's your original code without the IIFE but adding the message when each timer starts and showing how long it was between when the timer started and when it fired; this will still just do them roughly one second apart instead of doing what you said you want:
// Still doesn't do what you want, but shows that the IIFE isn't needed
// and when each timer is set up
let previousFired = Date.now();
for (let i = 1; i <= 5; i++) {
console.log(`setting timer to show ${i} in ${i * 1000}ms`);
const started = Date.now();
setTimeout(function () {
console.log(`${i} - fired after ${Date.now() - started}ms (${Date.now() - previousFired}ms after last)`);
previousFired = Date.now();
}, i * 1000);
}
If you want to wait one second to see 1, then wait a further two seconds to see 2, etc., either don't start the next timer until the previous one fires, or schedule them later. The latter is simpler, so let's do that:
let total = 0;
let previousFired = Date.now();
for (let i = 1; i <= 5; i++) {
total += i;
console.log(`setting timer to show ${i} in ${total * 1000}ms`);
const started = Date.now();
setTimeout(function () {
console.log(`${i} - fired after ${Date.now() - started}ms (${Date.now() - previousFired}ms after last)`);
previousFired = Date.now();
}, total * 1000);
}
You can handle async loops awaiting a timedout Promise, then you can call a external function:
function f(i) {
console.log(i)
}
const runLoop = async () => {
for(let i = 1; i <= 5; i++){
await new Promise(resolve => setTimeout(resolve, i * 1000))
f(i * 1000);
}
}
runLoop()
I try to animate the Clip-Path Property with JavaScript.
Means: It "grows" with help of SetTimeout.
I tried this, but it doesn't work.
http://jsfiddle.net/071dm2h3/8/
var el = document.getElementById("image")
function grow(i) {
el.style.clipPath = "circle("+ i +"px at 190px 160px)";
}
var i;
for(i = 0; i < 100; i++) {
setTimeout(grow(i), 400);
}
What am I missing here? Shouldn't the setTimeout change the value of I in every loop, so that I can see the result immediately?
To get this to work, I rewrote your setTimeout to be around your loop to make the image grow.
setTimeout( function(){
for(i = 0; i < 100; i++) {
grow(i)
}
},400);
Here is the jsfiddle
No, setTimeout fires once, and only once. Additionally, the way you are using setTimeout will lead to unintended results. You are basically saying:
I want to call grow(i) 400 milliseconds from now 100 times.
That means that 400 milliseconds from now, grow(i) will be called 100 times simultaneously. You don't want that, you want to call `grow(i) 100 times, waiting 400 milliseconds between each iteration.
Instead, you should either use setInterval which will wait a duration between each call or have each setTimeout schedule the next timeout after a duration.
Consider:
This will wait 2 seconds and then print 1,2,3,4 all at the same time.
for (let i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 2000);
}
This will print each iteration after a duration:
function iteration(i) {
console.log(i);
if (i > 3) return;
setTimeout(() => iteration(i + 1), 500);
}
iteration(0);
change you javascript to this
var el = document.getElementById("image")
function grow(i) {
console.log(i)
el.style.clipPath = "circle("+ i +"px at 190px 160px)";
}
var i;
for(i = 0; i < 100; i++) {
setTimeout( ((i) => () => grow(i))(i), i*10);
}
and kindly read this once
setTimeout in for-loop does not print consecutive values
I have written a script to get the top distance of the body to a div, I have 4 divs.
I'd like to set a time out and get each height every 5 seconds.
<div class="printOutputs" id="print1"></div>
<div class="printOutputs" id="print2"></div>
<div class="printOutputs" id="print3"></div>
<div class="printOutputs" id="print4"></div>
I get the ids
var ids = Array.prototype.slice.call(document.querySelectorAll('.printOutputs')).map(function (element) {
return element.id;
});
Created a for loop - can't seem to get each height every 5 seconds just returns 0s
for (var i=0;i<= ids.length;i++) {
var limit = ids.length;
var el = document.getElementById(ids[i]);
(function(x) {
setTimeout(function(){
console.log(getTopPos(el));
if(x === limit){
console.log('It was the last one');
}
}, 1000 + (3000 * x));
})(i);
}
Working example here:
https://jsfiddle.net/zidski/nLr9o1x2/
First, you have 2 loops that are both using the i variable for the iteration and the inner loop keeps overriding the outer i so that after the first outer iteration it ends up being 6 and the outer loop never gets to the second iteration.
Also, you escaped from the closure/scope problem by passing i as an argument to an Immediately-Invoked-Function-Expression so that when your callback runs it will refer to the correct i, but that was only half way because you need to pass the el variable (that is referencing the actual <div> element) as well (otherwise, it will always refer to the last-assigned element in the loop):
for (var j = 0; j <= limit; j++) {
(function(x, el) {
setTimeout(function() {
console.log(getTopPos(el));
}, 1000 + (3000 * x));
})(i, el);
}
See Fiddle
Why so complicated?? No need to map to the ids, and then find the ids ( why not simply keep the elements in the array?), also you can join the setTimeout and the IIFE into one:
var ids = document.querySelectorAll('.printOutputs');
for (var i=0;i<= ids.length;i++) {
setTimeout(function(x){//all in one...
var el = ids[i];
console.log(getTopPos(el));
if(x === ids.length-1){
console.log('It was the last one');
}
}, 1000 + (3000 * i),i);
}
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();
}
}