Can someone explain me the behaviour of following code and javascript
for(var i = 0; i < 5; i++)
{
setTimeout(function(){
document.write('Iteration ' + i + ' <br>');
},1000);
}
document.write('DONE!');
why does it print 'DONE!' first?
Shouldn't it print all values of the loop and then print 'DONE!'?
why does it print 'DONE!' first? Shouldn't it print all values of the loop and then print 'DONE!'?
No, you've explicitly told it not to; instead, you've just created five functions and called setTimeout five times to give them to the browser to call later.
This code:
setTimeout(function(){
document.write('Iteration ' + i + ' <br>');
},1000);
calls the function setTimeout, passing in the function you see. That function is not called at that time. It's just created. setTimeout will call it later, as is its job. (And when it does, it will blow away the document, because calling document.write after the main parsing of the page is complete implicitly does document.open, which wipes out the previous document.)
So here's what happens with that code:
The variable i is created.
It's set to 0.
A function is created.
setTimeout is called with that function object's reference being passed in, along with the value 1000.
i is incremented.
Steps 2 through 5 repeat for values 1, 2, 3, and 4 of i.
document.write is called with the value 'DONE!'
About a second later, the browser calls the first function created in the loop.
That function calls document.write, which implicitly does a document.open, which blows away the existing document and replaces it with Iteration 5 <br> (yes, really 5).
The second function created in the loop is run, outputting the message again.
The remaining three functions are called, adding the message another three times.
The reason we see Iteration 5 five times instead of Iteration 0, then Iteration 1, etc., is that the functions have an enduring reference to the variable i, not to its value when they were created, so they all read its value later, as of when they run.
Javascript in normal is Synchronous Exectue line by line, but with code change you can act sometime as Asynchronous, but it's not actually going to execute on a separate thread.
Ok, what if you want to print Done just after the Document.write is done
length = 0;
for(var i = 0; i < 5; i++)
{
setTimeout(function(index){
console.log('Iteration ' + index + ' <br>');
length++;
callback();
}(i),1000);
}
function callback(){
if(length == 5)
console.log('DONE!');
}
What we did here is we increment the counter and try to call the callback after each time its incremented and when counter reach 5 that mean all the 5 functions is called and then we are able to print Done.
And as a side and important note, you was trying to print 0, 1, 2, 3, 4 but actually when you run your code it will give you 5, 5, 5, ,5, 5 because you write i and i at the point when you print is reached 5 and that's why it go out of the for loop, but you notice the code i added, i pass i as an argument to the function so that it will save the value for us and when time to execute the funtion it will write 0, 1, 2, 3, 4
Related
In the following code, I tried to keep timeout but it doesn't work. I am sending array and expecting array index with 3 sec delay.
function displayIndex(arr){ // array as input
for(var i=0;i<arr.length; i++){
SetTimeout(function(){
console.log(i); // always returns 4
},3000);
}
}
displayIndex([10,20,30,40])
update:
var arr = [10,20,30,40];
function displayIndex(arr){ // array as input
for(var i=0;i<arr.length; i++){
setTimeout(function () {
var currentI = i; //Store the current value of `i` in this closure
console.log(currentI);
}, 3000);
}
}
displayIndex(arr); // still prints all 4.
Also, tried
arr.forEach(function(curVal, index){
setTimeout(function(){
console.log(index);
},3000);
}); // prints 0 1 2 3 but I do not see 3 secs gap between each display, rather one 3 sec delay before everything got displayed.
Use this:
function displayIndex(arr){ // array as input
var i=0;
var current;
run=setInterval(function(){ // set function inside a variable to stop it later
if (i<arr.length) {
current=arr[i]; // Asign i as vector of arr and put in a variable 'current'
console.log(current);
i=i+1; // i increasing
} else {
clearInterval(run); // This function stops the setInterval when i>=arr.lentgh
}
},3000);
}
displayIndex([10,20,30,40]);
1st: If you use setTimeout or setInterval function inside a for that's a problem 'couse all this are loops ways (the first two are loops with time intervals). Aaand setTimeout just run code inside once.
Note: setInterval need a function to stop it clearInterval, so that's why i put an if inside.
2nd: You are not setting currentI or i like a vector of arr operator. When you run an array the format is: arr[currentI], for example.
Doubts?
SetTimeout should be setTimeout. It's case-sensitive.
You're setting 4 timeouts all at once. Since you're incrementing the value of i every loop, it's going to be 4 at the end of the loop.
I'm not really sure what you're trying to do, but perhaps you wanted this?
setTimeout(function () {
var currentI = i; //Store the current value of `i` in this closure
console.log(currentI);
}, 3000);
The reason why it's behaving unexpectedly:
Case 1: In the first snippet, setTimeout() is adding the functions to the Event Queue to be executed after main thread has no more code left to execute. The i variable was passed as reference and, so the last modified value gets printed on each call since, it was passed by reference.
Case 2: In this case, since you are passing 4 explicit references, the values are different but, the execution order will same ( I.e., synchronous and instantaneous).
Reason: setTimeout() function always pushes the function passed to the queue to be executed with the delay acting as a minimum guarantee that it will run with the delayed interval. However, if there is code in the queue before the function or, any other code in the main thread, the delay will be longer.
Workaround: If you do not to implement blocking behaviour in code, I would suggest using an analogue of process.hrtime() for browser ( there should be a timing method on the window object and, write a while loop that explicitly loops until a second has elapsed.
Suggestion: I am somewhat confused as to why you need such blocking in code?
I want to create a simple game of sorts. I am trying to duplicate a div recursively after a few seconds. After duplicated, it creates the new div with a new unique ID (ID+i).
The idea is that it keeps creating divs and the user has to click on them to remove them for as long as they can before it reaches the max (game over).
It won't properly wait to create the divs. I want to create new divs from the existing one every few seconds, but it either creates all 15 as soon as I run it or it only creates 1 and stops there.
JSFIDDLE -
https://jsfiddle.net/namelesshonor/msrkxq63/
function spawnFly() {
if(x >= 15){
alert("YOU LOST\n15 Flys have infested your screen!");
}
else if(x < 15) {
x++; // adds another fly to the counter
setTimeout(duplicate(), 2000); // spawns a new fly after a few secs
animateDiv(); // animate the spawned fly
spawnFly(); // called recursively until fly count is met
}
};
function duplicate() {
var original = document.getElementById('fly'+i);
var clone = original.cloneNode(true);
clone.id = "fly" + i++;
clone.onclick = swat;
original.parentNode.appendChild(clone);
};
function animateDiv(){
var newq = makeNewPosition();
var oldq = $('.shoo').offset();
var speed = calcSpeed([oldq.top, oldq.left], newq);
$('.shoo').animate({ top: newq[0], left: newq[1] }, speed, function(){
animateDiv();
});
};
The argument to setTimeout should be the function pointer to duplicate, not the result of calling the duplicate function.
setTimeout(duplicate(), 2000);
should be
setTimeout(duplicate, 2000);
Also, you might be intending to call the spawnFly function in the timeout, not the duplicate function. The duplicate function would then be called immediately to "spawn" a new fly. Then in 2 seconds, the spawnFly function is called to duplicate another fly and queue spawnFly again. The way you currently have it set up, the it immediately recurs into the spawnFly function, queuing up 15 flies to spawn in 2 seconds and immediately topping out the fly count (x)
Also, you're your increment of i causes an off by 1 error such that you're always trying to assign the value of the next fly to original. You should use pre-increment (++i) instead of post-increment (i++) to get your desired result
All changes applied:
https://jsfiddle.net/msrkxq63/3/
When you call setTimeout in your example, you're passing the result of duplicate(), not the function duplicate itself as the callback. As duplicate does not return anything, setTimeout tries to call the function undefined. You could either call it this way (as an anonymous callback):
setTimeout(function() { duplicate }, 2000)
or simply,
setTimeout(duplicate, 2000)
If you notice duplicate() in setTimeout(duplicate(),2000);,
it's a function call.
setTimeout 's first parameter is a function.
If you pass duplicate(),
it gets evaluated before the wait and looks for the return value and calls that.
Function or not, it waits after the function call and ends up doing nothing afterthe wait.
So we can say the flow is:
1. Callback = duplicate()(duplicate is called before wait) = <return value of duplicate> instead of the function duplicate itself.
2. Milliseconds = 2000.
3. Call return value after 2 seconds.
The correct code is:
setTimeout(duplicate,2000)//Note that there are no brackets here
My objective is to populate an array dynamically using setInterval function. So I created a global variable array that I can access outside the scope of setInterval function. But when I try to do console.log, its empty.
However, if I try to do console.log inside the setInterval function, I can see the array populating every 1000 ms.
Its supposed to be a global variable so i should be able to see the variable right? How come it cannot be seen outside the function?
below is the code:
var array = [];
var n;
setInterval(function(){
n = Math.random();
if(n < 0.5){
array.push('white');
}else{
array.push('black');
}
// console.log(array); // i can see the array here
}, 1000);
console.log(array); // but i cannot see the array here
UPDATE:
Ok I now know the reason why its empty. So when I put a setTimeout of 5secs, I can now see the contents.
setTimeout(function(){
console.log(array); // i can now see the array!
}, 5000);
I'm pretty sure the array you "can't see" is actually displaying the correct array, an empty one. It gets called before the functions first array.push, so it shows up as empty.
Because you are using setInterval, with a 1 second delay, to add to the array the last line console.log(array); is executed before any of the interval function so the array would still be empty then.
Good info here: http://javascript.info/tutorial/settimeout-setinterval
setInterval schedules a function to be executed asynchronously. This means that the following code is executed first, finished, and only then, some time later, the scheduled function is started.
the reason why I cannot see the array is because theres not enough time for the console to log the contents so by putting another setInterval or setTimeout to delay the execution of console.log, I can now see the contents.
var array = [];
var n;
setInterval(function(){
n = Math.random();
if(n < 0.5){
array.push('meron');
}else{
array.push('wala');
}
// console.log(array); // i can see the array here
}, 1000);
setInterval(function(){
console.log(array); // i can now see the array!
}, 2000);
i was a reading book named async javascript , one of the example of this book tried to show that events in javascript will trigger when processor has no instruction to do in the same process . for the proof of his word he brought an example :
for (var i = 1; i <= 3; i++) {
setTimeout(function(){ console.log(i); }, 0);
};
the output is :
4
4
4
4
everyhting is ok and i expected the result . but when i put the "console.log(i)" out of function the result will change .
for (var i = 1; i <= 3; i++) {
setTimeout(console.log(i), 0);
};
the output is :
1
2
3
i don't know why the output is different in those examples . i know its about variable scopes , but don't know exactly the reason . i hope if someone can help.
i don't know why the output is different in those examples
Because in the second case you are calling console.log(i) immediately and pass the return value undefined to setTimeout (instead of passing a function like in the first case).
Arguments are always evaluated first, so if you have foo(bar()), bar is called first and its return value is passed to foo.
You can verify this by adding console.log('after'); after the loop. In the first case, you should get
after
4
4
4
and in the second case, you will get
1
2
3
after
demonstrating that setTimeout is not adding anything to the event queue.
See also Calling functions with setTimeout()
i know its about variable scopes
Actually it's not, it's all about timing, the moment when console.log is executed.
When you call setTimeout(expr,t), expr gets evaluated, hoping it returns a function. In your second case console.log(...) doesn't return anything, but it gets evaluated nonetheless. What you see is the side-effect of this evaluation prior to passing the result to setTimeout.
I am at a loss how best to approach for loops in JavaScript. Hopefully an understanding of for loops will help shed light on the other types of loops.
Sample code
for (var i=0; i < 10; i=i+1) {
document.write("This is number " + i);
}
My understanding is that when i has been initialized, it starts with the value of 0 which then evaluated against the condition < 10. If it is less than 10, it the executes the statement document.write("This is number + i); Once it has executed the preceding statement, only then does it increment the next value by 1.
Guides I have consulted:
http://www.functionx.com/javascript/Lesson11.htm
http://www.cs.brown.edu/courses/bridge/1998/res/javascript/javascript-tutorial.html#10.1
http://www.tizag.com/javascriptT/javascriptfor.php
Now the guide at http://www.functionx.com/javascript/Lesson11.htm seems to indicate otherwise i.e.
To execute this loop, the Start condition is checked. This is usually
the initial value where the counting should start. Next, the Condition
is tested; this test determines whether the loop should continue. If
the test renders a true result, then the Expression is used to modify
the loop and the Statement is executed. After the Statement has been
executed, the loop restarts.
The line that throws me is "If the test renders a true result, then the Expression is used to modify the loop and the Statement is executed". It seems to imply that because 0 is less than 10, increment expression is modified which would be 0 + 1 and THEN the statement, e.g. document.write is executed.
My problem
What is the correct way to interpret for loops? Is my own comprehension correct? Is the same comprehension applicable to other programming languages e.g. PHP, Perl, Python, etc?
Think of a for loop as the following
for(initializers; condition; postexec) {
execution
}
When the loop is first started the code var i = 0 is run. This initializes the variable that you will be testing for inside the loop
Next the loop evaluates the i < 10 expression. This returns a boolean value which will be true for the first 10 times it is run. While this expression keeps evaluating to true the code inside the loop is run.
document.write("This is number " + i);
Each time after this code is run the last part of the loop i++ is executed. This code in this example adds 1 to i after each execution.
After that code is executed the condition of the loop is check and steps 2 and 3 repeat until finally the condition is false in which case the loop is exited.
This the way loops work in the languages you mentioned.
Lets have a look at the corresponding section in the ECMAScript specification:
The production
IterationStatement : for ( var VariableDeclarationListNoIn ; Expressionopt ; Expressionopt) Statement
is evaluated as follows:
1. Evaluate VariableDeclarationListNoIn.
2. Let V = empty.
3. Repeat
a. If the first Expression is present, then
i. Let testExprRef be the result of evaluating the first Expression.
ii. If ToBoolean(GetValue(testExprRef)) is false,
return (normal, V, empty).
b. Let stmt be the result of evaluating Statement.
...
f. If the second Expression is present, then
i. Let incExprRef be the result of evaluating the second Expression.
ii. Call GetValue(incExprRef). (This value is not used.)
As you can see, in step 1, the variable assignment is evaluated. In step 3a, the condition is tested. In step 3b, the loop body is evaluated, and after that the third expression is evaluated in step 3f.
Therefore your understanding of the for loop is correct.
It is to assume that it works the same way in other languages, since the for loop is such a common statement in programming languages (note that Python does not have such a statement). But if you want to be absolutely certain, you better consult their specification as well.
Your quoted source is wrong, and we can prove it...
The basis of the for loop has four separate blocks which may be executed:
for(initialise; condition; finishediteration) { iteration }
Fortunately we can execute a function in each of these blocks. Therefore we can create four functions which log to the console when they execute like so:
var initialise = function () { console.log("initialising"); i=0; }
var condition = function () { console.log("conditioning"); return i<5; }
var finishediteration = function () { console.log("finished an iteration"); i++; }
var doingiteration = function () { console.log("doing iteration when `i` is equal", i); }
Then we can run the following, which places the above functions into each block:
for (initialise(); condition(); finishediteration()) {
doingiteration();
}
Kaboom. Works.
If you viewing this page using Safari on the Mac then you can AppleAlt + I and copy the above two snippets, in order, into the console and see the result.
EDIT, extra info....
Also... the finished iteration block is optional. For example:
for (var i=0; i<10;) {
console.log(i); i++;
};
does work.
The second reference is wrong. Your explanation is correct.
Another way to think about it, if this helps you:
var i = 0;
while (i < 10) {
document.write("This is number " + i);
i++;
}
This is for statement syntax:
for(initalize, condition, increment) {
Do_some_things();
}
initalize Will executed only one time when for begin then it execute Do_some_things(); statement, and while condition still true it will execute increment and then Do_some_things();. if co condition false, for would exit.
for (var i=0; i < 10; i=i+1) {
document.write("This is number " + i);
}
var i=0 will execute one time (initalize).
i < 10 condition was always checked after a loop.
i=i+1 will execute after check i < 10 and result is true.
Value of i is: 0, 1, 3, 4, 5, 6, 7, 8, 9 (10 times loop)