For loop, Let, and setTimeout - javascript

for (let i=1; i<=5; i++) {
setTimeout( function timer(){
console.log( i );
}, i*1000 );
}
Biggest thing I'm confused about here - Why does this print 1,2,3,4,5 - All one second apart? Rather than incrementing one second, and taking one second longer each time we go through the for loop? I also have another variation that confuses me, where I do a console.log(i) as the last step in the for loop, and it just instantly posts 1-5. Any help here?
Coming from C#, for the record, not sure if the behaviour exists in C# or if I'm just a bit rusty.
Thanks!

It is actually quite simple. The for loop schedules the timer function for all five values. Then the timer function starts printing the numbers. Now the reason for them being printed 1 second apart is i*1000 in setTimeout. As a result, 1 will be printed 1 second after it has been scheduled, 2 will be printed 2 seconds after it has been scheduled, and approximately 1 second after 1 has been scheduled, and so on...
See the snippet below to understand how it works. Keep in mind that setTimeout does not block the execution of the for loop.
for (let i=1; i<=5; i++) {
setTimeout( function timer(){
console.log( i );
}, i*1000 );
console.log("Print " + i + " after " + i +" seconds.");
}
console.log("for loop completed.");

setTimeout() will place your timer() into the queue.
for loop execute very quickly nearly immediately, each loop timer() will be placed into the queue with delays: 1,2,3,4,5 seconds respectively.
console.log(i) will print 1-5 because of the immediate execution of for loop.

I guess what the other answers have not touched upon is the event loop.
The event loop is a distinct execution thread than the main JS thread.
Your for loop would execute in the main JS thread, pushing each timeout on the event loop which would be executed in the event loop as scheduled. The time scale of events would look something like this.

I hope this is what you are looking for
System.Threading.Thread.Sleep(1000*i);

Related

closure and for loops consuing me

alright guys. every singe time i think i understand fundamental JS quirks i go insane and realize i don't at all... while the part of this that usually stumps people DOES NOT for me... i am so confused as to why this console.logs '10' ten times. WHY is it not '9' 9 times... the loop clearly says i < 10.... i don't understand why the loop runs an extra time to ten here...
for (var i=0; i < 10; i++){
setTimeout(function(){
console.log(i);
}, 1000);
}
to add to that why does this loop below respect the i<10 thing and produce 1,2,3,4,5,6,7,8,9.. clearly in the example below the loop respects the 'until i is less than 10 (which is 9) why does it not do that in the first example and run until i is 10?
for(var i=0; i<10; i++){
console.log(i)
}
everytime i think i understand this stuff i don't its actually starting to drive me a bit insane. ive been coding over a year now and still am confused by this. i understand the call stack stuff and the closure to a degree. most people get stumped and say it will print 9 every second etc... i get that part. but why is it 10 and not 9?! also plz in layman's terms as best you can. i understand that the ++ part actually runs at the beginning of each iteration (except for the first) but i still am so confused as to why ANYTHING with closure or call stack etc would create an extra run of the loop.
also if you are REALLY up for it too... why THE F*** does this produce '1' and not '2' lol...... please answer the first question though. that is my primary confusion right now
for(let i=0; i<2; i++){
setTimeout(()=>console.log(i))
i++
}
actually at last iteration the for loop increment i value to 10. then the for loops evaluates the condition i < 10. since i is now 10 the block statement doesn't run again, but its current value is 10.
the reason the second one prints 1,2,3,4 and the first prints 10,10,10 is because the second one the code is sync, while the first one is async (setTimeout). setTimeout will be only be executed after the callstack empties. by that time i will be 10. since i is defined as var, which is not blocked scoped like let, all console.log(i) prints 10.
the second goes until the 9 because once it reaches the last iteraction i is 9 and prints 9. Then i is updated to 10 and the next block doesn't run because now i < 10 is no more valid. i is now 10 but there is no more console.log to be executed. if you console.log(i) after the for loop will print 10.
Meanwhile the async code, each setTimeout can only execute console.log once the callstack from the event loop is clear. when the event loop is clear, the for loop already has finished its execution and i is now 10. now each console.log will be executed and the value they find for i is 10.
A for loop is (more or less) the same to a while loop, and when looking at that we might find the answer:
let i = 0;
while(i < 10) {
console.log("inside loop", i); // 0,1,...,9
i++;
}
// at this point, the loop exited with i = 10
console.log("after loop", i);
On the last iteration, i is 9 when passing the body, then it gets incremented to 10, then 10 < 10 evaluates to false and the loop exits. Then somewhen the timeout calls back and 10 gets logged.

Why does setTimeout not execute properly inside a loop VS inside a recursive function

This code waits one second and executes all the iterations at once:
for(let i=0;i<4;i++){
setTimeout(function(){console.log("Hello")},1000)
}
This code executes properly as expected:
var i = 0;
function loop(){
setTimeout(function(){
console.log("Hello" + " " + Number(i+1))
i++
if(i<3){
loop()
}
},1000)
loop()
My question is why? Does this have to do with the synchronous single-threaded nature of JavaScript? how so? An explanation as to why this is happening is my question.
Your first code does this:
"Set four timeouts to go off in one second from now"
Your second code is:
"Set a timeout and alert the value of i, increment i and if the loop isn't over then set a new timeout"
Quite a difference! The key thing is, setting multiple timeouts isn't like a queue, they will all start from when you call them.
You probably want to multiply the delay by i
var f = function(){console.log("Hello")};
for (let i=0; i<4; ++i) setTimeout(f, i * 1000);

For Loop Iteration Happening all at Once Rather than By Delay

For one of my elements on my page, I want the text to change every ten seconds, and for the class to be changed. Text changing is easy, as is class changing, but I'm having trouble with my for loop, and I feel like I'm missing something.
I want to have the for loop choose a random faction in an array, and then apply that to the element. For my testing, I've been using console.log rather than DOM manipulation.
First, I set up my array:
var factions = ["Enforcers", "Reapers", "Ular Boys", "Roaches"];
Then, I want a variable that is a number chosen at random in reference to this array:
var x = factions[Math.floor(Math.random()*factions.length)];
From that, I want the ability to run the Math.floor and Math.random functions elsewhere.
function reDefine() {
x = factions[Math.floor(Math.random()*factions.length)];
console.log(x);
}
Finally, I want the for loop to run 200 times (I've chosen 200 times because it's far and beyond the time the user will be staying on the site), so I told it to count to 200 (i = 0; i < 200). After that, I wanted each time it iterated, to wait 10s, so I have a Timeout function with a delay of 10000 (milliseconds). Then, the code to reDefine and then, in the case of testing, console.log the new definition of the x variable.
function reChange() {
for (var i = 0; i < 200; i++) {
setTimeout(function() {
reDefine();
console.log("Chosen faction is now: " + x);
}, 10000);
}
}
Instead of counting to 1 (the first iteration), waiting 10000, and then redefining x, it redefines x two hundred times, then logs them all.
Is there something I'm specifically doing wrong here, perhaps with the Timeout function?
Is there something I'm specifically doing wrong here, perhaps with the Timeout function?
Yes! You're scheduling a bunch of deferred callbacks, but not actually waiting until one has finished to schedule the next.
You can fix that with something as simple as:
function reChange(currentIndex) {
setTimeout(function() {
reDefine();
console.log("Chosen faction is now: " + factions[currentIndex]);
// If we haven't gotten to the end of the list, queue up another one
var nextIndex = ++currentIndex;
if (nextIndex < factions.length) {
// Enqueue the next faction
reChange(nextIndex);
}
}, 10000);
}
Make sure to note that the function without the timeout has closure over the value of currentIndex for each call of reChange. That is, the next invocation does not replace currentIndex in any previous timeout, since primitives (including numbers) are passed by value. Closure in JS can be a tricky thing.
The core problem is that your execution right now looks like:
for each item
wait
log
rather than:
for the current item
wait
log
repeat
Because JS is single-threaded (for most intents and purposes), setTimeout adds a callback to be executed later. It doesn't block until the timeout has expired, like a traditional sleep would do.

do while loop with setTimeout causing infinite loop

I'm working on a simon game(the one you follow the color pattern). It makes it through the computers 1st turn and my 1st turn but trying to do a setTimeout between each computer choice is causing an infinite loop with the do while statement or playing both choices at the same time if I use for loop. The highlightDiv function just does a toggleClass on the div, then a setTimeout to toggle the class back off. And the audioStart function uses a switch statement to determine which sound to play and then a setTimeout of half a second to play that sound. I thought this setTimeout on the increment would allow enough time for those two things to happen before it incremented and then did the next index in the computerChoice array. This is the codepen if that is easier: http://codepen.io/RawleJuglal/pen/pgRVKd
var computerChoice = ["red", "yellow"],index=0;
function computerPattern(cPattern, index){
console.log("Entered computer pattern function");
console.log("This is cPattern");
console.log(cPattern);
console.log("This is index: "+ index);
if(index !== cPattern.length)
{
highlightDiv(cPattern[index]);
audioStart(cPattern[index]);
index++;
computerPattern(cPattern, index);
}
else
{
index=0;
}
console.log("Leaving computerPattern function");
}
computerPattern(computerChoice, index);
Javascript is single threaded, and the concept of timeouts means that you place a function on a special queue, which is to execute your callbacks when their time is due. now, since in your code, the i variable is being updated only in the timeout function, which is only after 3 seconds, it means that the body of the loop will run again and again until 3 seconds are met.
in 3 seconds, javascript can run thousands of iterations, and each iteration registers another timeout, which means that your event queue is blasted and your single thread will have hard time completing all those tasks until i is finally reaching cPattern.length, if ever.
your solution might be using some sort of setInterval which has a callback that does what you want, and stops on some iteration variable that increments every time, let's say like this:
var interval = setInterval(function(){
console.log(cPattern[i]);
highlightDiv(cPattern[i]);
audioStart(cPattern[i]);
i++;
if(i >= cPattern.length){
clearInterval(interval);
}
},
2000);
Your timeout function is never called because you don't give it a chance. As long as your code is running (your loop), the browser can't run the scheduled scripts, which use the same thread. You will have to rethink the code.
You're incrementing a variable called i within the anonymous function delegate that you're passing to setTimeout. The locally-scoped variable i on which the do-while loop depends is never updated.
Depending on the functionality you're looking for, you can increment the variable i while it's still in scope, then use a closure to pass a snapshot of its value to the setTimeout function delegate.
function run(){
var i = 0
do{
(function(i){
setTimeout(function(){
alert(i);
},1000);
})(i++)
}
while(i < 2);
}
run();

setInterval with an Array

I'd like to use the setInterval function in jQuery in order to create an alert with the content of one array every 4 seconds. However my alerts show all the values of my array within a short amount of time and it stops for 4 seconds after having displayed all the values.
$.each(['html5', 'EDM', 'Coca Cola', 'creativity'], function(id,value) {
setInterval(function(){
alert(value);
}, 4000);
});
In this case, I'd like to display something like : Alert('html5') - 4 seconds - Alert('EDM') - 4 seconds - Alert('Coca Cola') - 4 seconds - Alert('creativity') - 4 seconds - Alert('html5') - 4 seconds - Alert('EDM') - 4 seconds - ...
Move the setInterval from the loop.
var arr = ['html5', 'EDM', 'Coca Cola', 'creativity'];
var index = 0;
setInterval(function() {
console.log(arr[index++ % arr.length]);
}, 4000);​
Live DEMO
No jQuery needed.
Use a recursive setTimeout
var arr = ['html5', 'EDM', 'Coca Cola', 'creativity'];
var alertLoop = function(i) {
if (arr[i]) {
alert(arr[i]);
setTimeout(function(){alertLoop(i+1);}, 4000);
}
}
alertLoop(0);
Demo: http://jsfiddle.net/B5tJw/
Use of setInterval is discouraged. For an explanation, read here: http://bonsaiden.github.com/JavaScript-Garden/#other.timeouts
To summarise the problem:
setInterval fires the event at a regular interval, regardless of what else is happening on the page.
However, Javascript is not multi-threaded: it can only run one code sequence at a time. If setInterval is triggered while another code sequence is being run, the new code sequence will be blocked until the previous one is finished, and will wait for it.
If this happens repeatedly, you can end up with a large number of events waiting to be run.
You're using alert() to display your messages. alert() causes the code execution sequence to pause until the user responds to the message, but keeps it locked, so that other events cannot run their code. This is a particular problem for setInterval, because it fires new events at the specified time, regardless of whether something else is blocking the script.
The solution is to use setTimeout instead of setInterval.
setTimeout is only triggered once, but it is easy to tell it to trigger itself again inside its own function, so you can get the same effect as setInterval, but with much more control. Your code can wait until after the alert() has been accepted by the user before triggering the next event, which means that you won't get the problem of cascading events that can happen with setInterval.
Hope that helps explain things. The link I mentioned at the beginning is also very helpful.

Categories

Resources