I'm new to learning javascript and getting strange behaviour that I don't understand
So this is printing exactly what I expect. 0,1,2,3,4
var numberOfPlayers = 5;
for ( i = 0; i < numberOfPlayers; i++ ) {
alert(i);
}
But when I try the following code where I want to run a function every 1 seconds I get, 5,5,5,5,5
var numberOfPlayers = 5;
for ( i = 0; i < numberOfPlayers; i++ ) {
setInterval(function () {
alert(i);
}, 1000);
}
Can anyone explain to me what is actually happening here. I would expect the same numbers in both parts of code.
In the first instance, you alert the value of i as it goes around the loop.
In the second, you alert the value of i after one second. By the time that second has passed, the loop has finished going around five times, so the value of i at the time is the last value of i.
The I is not bound in the inner function inside the loop -- to something like this to bind it in each iteration of the loop
var numberOfPlayers = 5;
for ( i = 0; i < numberOfPlayers; i++ ) {
(function(i) {
setInterval(function () {
alert(i);
}, 1000);
})(i);
}
You should be getting 5 5 5 5 5. The reason is that your for loop very quickly runs and binds the function to run 5 times. It iterates through itself until i is equal to 5. By the time the second is up and the function is scheduled to run, i is equal to 5. It will then repeat every 1 second 5 times.
As #Quentin explained in his answer, it's an issue of timing and because of the closure inside of setInterval(). There's a good explanation here: Javascript infamous Loop issue?.
For a case like this I would probably use the code #Soren suggested. Another option would be to use a separate function:
var numberOfPlayers = 5;
for ( i = 0; i < numberOfPlayers; i++ ) {
intervalAlert(i);
}
function intervalAlert(i) {
setInterval(function () {
alert(i);
}, 1000);
}
See also http://blog.mixu.net/2011/02/03/javascript-node-js-and-for-loops/
Related
I'm attempting to make a function wherein I console.log a phrase when the number is between 0-10.
I'm getting it to print out the phrase 10 times, but to get it to pass the testing platform, I need to print it out when the number is 0, and it's not passing.
function doWhileLoop(num) {
var i = 0;
function incrementVariable() {
i++
return i;
}
do {
console.log(i, "I run once regardless.");
}
while (incrementVariable() <= 9);
}
doWhileLoop(10);
Only print when incrementVariable() is less than the variable received:
function doWhileLoop(num) {
var i = 0;
function incrementVariable() {
i++
return i;
}
do {
console.log("I run once regardless.");
}
while(incrementVariable() <= num);
}
The point is that the loop condition must muse the parameter that is passed to the doWhileLoop function (while(incrementVariable() <= num)). And I guess the exercise is trying to show you that the code inside the do block will always run at least once, even if the condition in the while is false. So the following code runs once:
do {
console.log("I run once regardless.");
}
while(false);
Making this not count past 9
function doWhileLoop(num) {
if(num>9) {
num = 9;
}
var i = 0;
function incrementVariable() {
i++
return i;
}
do {
console.log("I run once regardless.");
}
while(incrementVariable() <= num);
}
Note, it is not clear to me whether you want it to print 10 times either 0 or 10 cannot be included. If you want it to print 0..10 that would actually be 11 times. So adjust the examples below as appropriate.
Note that while troubleshooting, it is helpful to print the value i, as I did in the examples below. When the program works as expected, you can then remove i from your console.log before for your final submission.
I also print out the final value of i when the loop is exited.
This is a god point of comparison for understanding how the loops, and the incrementation of values work in each case. It's also good to know how these loops leave variables that may be accessed later in a program, and serves as a comparison to the values printed.
The first two examples do not use a separate function just to increment, since incrementation itself is a function, and is built into the language. It also highlights how two variations the language provides for incrementing can be exploited in useful ways to control endpoint conditions such as this.
In all cases I consistently used i < num so the comparisons would hold.
Depending on your requirements, this could also be i < num+1 (equivalent to i <= num), or i < num-1. Adjusting this will increase or decrease the last/highest value of i that is printed, (and the value i has after the loop ends).
++i prints 10 times: when i is 0..9.
i === 10 when the loop ends.
(using i <= num would print 0..10 and exit with value 11)
function doWhileLoop(num) {
let i = 0;
do {
console.log(i, "I run once regardless. ");
}
while (++i < num);
console.log("Final value of i:", i);
}
doWhileLoop(10);
For comparison, this version,
i++ prints 11 times: when i is 0..10.
i === 11 when the loop ends.
(using i < num+1 would print 0..9 and exit with value 10)
function doWhileLoop(num) {
let i = 0;
do {
console.log(i, "I run once regardless. ");
}
while (i++ < num);
console.log("Final value of i:", i);
}
doWhileLoop(10);
The difference between ++i and i++ is that:
++i the increments i first, then uses new value to determine whether to run the loop again or not.
i++ determines whether or not to run the loop again first, based on the value i already has (and printed), then it increments i afterwards, but before runs the loop again (or before it exits, if the loop is not be be run again).
In your code, incrementVariable() acts exactly like my ++i example above, because the incrementVariable() function call is executed before the while comparison is made.
Another solution would be to use an if-then to constrain your print statement.
This version prints 10 times: when i is 0..9.
i === 10 when the loop exits.
(In this scenario you can control the final value of the loop, and the number of times the loop is run, independent from the values that are printed.)
function doWhileLoop(num) {
var i = 0;
function incrementVariable() {
i++
return i;
}
do {
if (i < num) {
console.log(i, "I run once regardless.");
}
}
while (incrementVariable() < num);
console.log("Final value of i:", i);
}
doWhileLoop(10);
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 3 years ago.
I have 2 simple code snippet about for loop involving let and var separately.
First code which has a variable declared with let
for (let i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
so it will show o/p like this
0123456789
but if I replace let with var like this
for (var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
it will print 10 for ten times.
I know it is something related to function level scope and block-level scope, but want to clearly understand step by step process of execution.
Thanks in advance.
The reason why you are only printing 9 is that the callback function is executed after the loop is done. Which means that i is 9.
You can:
If you are trying to print 1 - 10 after 1 sec, you can loop in the callback function. Like:
setTimeout(function() {
for (var i = 0; i < 10; i++) { //Put the loop inside the setTimeout db function.
console.log(i);
}
}, 1000);
If you are trying to print every one sec, you can pass the i as the 3rd parameter on setTimeout
for (var i = 0; i < 10; i++) {
setTimeout(function(o) { //Receive it on variale o
console.log(o);
}, 1000 * i, i); //Pass the i as third parameter
}
Doc: setTimeout
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 6 years ago.
I am writing a function to fire a series of click handlers with a given time interval in between.
function play(step){
var buttons = document.querySelectorAll('.age');
buttons[0].click();
for (var i = 1; i < buttons.length; i++){
setTimeout(function(b){
b.click();
}, 300 + (step*i), buttons[i]);
}
}
The above function works as intended, however, the following version which to me seems like it should be equivalent, does not work:
function play(step){
var buttons = document.querySelectorAll('.age');
buttons[0].click();
for (var i = 1; i < buttons.length; i++){
setTimeout(function(b){
console.log(i);
console.log(b);
b[i].click();
}, 300 + (step*i), buttons);
}
}
I was getting the following error:
TypeError: Cannot read property 'click' of undefined
After checking, I find that console.log(i) is printing 6. So, apparently, the attribute access isn't occurring until after the loop is over, which explains the error! But what exactly is going on here? I'm relatively new to javascript, but is this behavior the result of the anonymous function acting as a closure? That doesn't sound like the right explanation to me. Is it because setTimeout is delaying the evaluation of the anonymous function?
ETA:
I did an experiment:
function sleep(ms){
var current = new Date().getTime();
while (current + ms >= new Date().getTime()){};
}
function play(step){
var buttons = document.querySelectorAll('.age');
buttons[0].click();
for (var i = 1; i < buttons.length; i++){
setTimeout(function(b){
console.log(b);
console.log(i);
b[i].click();
}, 300 + (step*i), buttons);
sleep(1000);
}
}
when I run play(200) I get the same error message, so sleep(1000) should be enough time to make sure that the loop hasn't exited before the first timeout is up, no?
It's because variable in for loop is the same in each iteration (same reference) and when setTimeout is running the for loop has ended so the i variable will be last value of the loop, to fix this you can create a closure with i variable:
function play(step){
var buttons = document.querySelectorAll('.age');
buttons[0].click();
for (var i = 1; i < buttons.length; i++){
(function(i) {
setTimeout(function(b){
console.log(i);
console.log(b);
b[i].click();
}, 300 + (step*i), buttons);
})(i);
}
}
I have a problem with pausing asynchronous JS loop from execution for some period of time.
I need it to pause, for example every 45th iteration so I came up with a simple code to help me determine when it happens:
if (i > 1)
{
var numba = i / 45;
if (isInteger(numba))
{
document.write('TIMEOUT START <br>');
setTimeout(function(){document.write('TIMEOUT END<br>');}, 540000);
}
}
function isInteger(x)
{
return x % 1 === 0;
}
And my async loop code is following:
var num = 150;
var asyncLoop = function(o)
{
var i=0;
var loop = function(){
i++;
if(i==o.length){o.callback(); return;}
o.functionToLoop(loop, i);
}
loop();//init
}
asyncLoop({
length : num,
functionToLoop : function(loop, i)
{
if (i==0){}
loop();
},
callback : function(){
}
});
First I tried to place this code for pausing loop inside the loop function however that didn't work and now I partly understand why. However every other way I tried failed also.
Does anyone know how to achieve what I'm trying to do?
Thanks in advance
Here's a snippet I whipped up.
It will call functionToLoop loops times, pausing for pauseTimeout milliseconds each pauseAt runs, then calls callback at the end.
function loopWithPause(functionToLoop, callback, loops, pauseAt, pauseTimeout) {
var n = 0;
function start() {
if(n + pauseAt > loops) pauseAt = loops- n;
for(var i = 0; i < pauseAt; i++) {
functionToLoop(n + i);
}
n += pauseAt;
if(n == loops) callback();
else setTimeout(start, pauseTimeout);
}
start();
}
Example:
loopWithPause(function(i){console.log(i)}, function(){console.log('done!')},10, 2, 2000);
Outputs:
0
1
(2 second pause)
2
3
(2 second pause)
4
5
(2 second pause)
6
7
(2 second pause)
8
9
done!
For your scenario the call will probably be:
loopWithPause(functionToLoop, callback, 150, 45, 540000);
This does what you're asking. Perhaps you can adapt it to your need. Paste it into your JavaScript console and watch it run forever.
var printNumbers = function(startingAt) {
var end = startingAt + 45;
for (i = startingAt; i < end; i++)
{
console.log(i)
}
window.setTimeout(function() { printNumbers(end); }, 1000)
}
printNumbers(0)
It's almost recursive, except that the recursion happens via the event loop with the time-out.
This carries state forward via the variable, to be used next time the function runs (alternatively use a global variable or closure to contain the state between function executions). It's the only way to do something like this, because JavaScript is single-threaded and you can't 'pause' without making the whole runtime grind to a halt.
The first example below is #62 from John Resign`s Learning Advanced JavaScript http://ejohn.org/apps/learn/#62. It is called Fix the Broken Closures. Example 1 fails 4 times. Example 2, which is only different because it has a wrapper function, passes 4 times. It is example #63 from the same tutorial
Can someone please explain
1) why i == count++ in example 1 fails.
2) why i == count++ passes with the help of the wrapper function. How does the wrapper function change things to make it work?
Thanks in advance.
Example 1
var count = 0;
for ( var i = 0; i < 4; i++ ) {
setTimeout(function(){
assert( i == count++, "Check the value of i." );
}, i * 200);
}
Example 2
var count = 0;
for ( var i = 0; i < 4; i++ ) (function(i){
setTimeout(function(){
assert( i == count++, "Check the value of i." );
}, i * 200);
})(i);
This is pretty straight forward.
Since setTimeout executes "asynchronously", there is no way of telling the exact value of i when the function executes, since the loop carries on running.
By using a function wrapper, effectively you are treating the body of the call as a function and are EXPLICITLY passing in the value of i.
You could clear this up by renaming the function i param to j or something else and update the innards of the function to from i to j
Basically it boils down to scoping
Since i is being incremented in the loop the odds are strong that each time the setTimeout callback is invoked the value of i will be 4.
The wrapper function introduces a new scope allowing the value of the parameter i to maintain its value even though the surrounding i is being incremented by the loop.
function outerScope() {
var x = 2, y = 3;
function innerScope() {
var x = 3;
// Obviously this alerts 3.
alert(x);
// Since we have no 'y' defined, alert the value 3 from the outer scope.
alert(y);
}
// Introduce a new scope.
innerScope();
// Since we have left the inner scope x is now 2.
alert(x);
// Obviously this alerts 3.
alert(y);
}