Clarification on how setTimeout & forloop interaction works - javascript

I am having trouble understanding why the answer is 9. From my understanding adding the setTimeout function into a for loop means that the setTimeout function will run 3 times for each loop. So it loops 3 times and in each loop, setTimeout runs 3 times for a total of 9 times? I am missing the understanding.
var result = 0;
for (var i = 0; i < 3; i++) {
setTimeout(function() {
result += i;
}, 1000);
}
console.log(result) // answer is 9

Javascript is a single-thread system. setTimeout basically puts your request on the call stack to be executed after 1 second.
It is still fired in the scope of your function, but at the end of it, with a 1 second delay.
At the end of your function, i === 3 and because you execute it 3 times, you'll get result === 9
You can see this more clearly if you do this:
var result = 0;
for (var i = 0; i < 3; i++) {
setTimeout(function() {
result += i;
console.log("i: " + i);
console.log("result: " + result)
}, 1000);
}
You can wrap up your timeout, to break the dependency on i.
Do this by placing setTimeout in a function and pass i into it. that way, it doesn't matter what i is,
as we are using j which is scoped inside the new function.
var result = 0;
for (var i = 0; i < 3; i++) {
(function(j){
setTimeout(function() {
result += j;
console.log("i: " + i);
console.log("j: " + j);
console.log("result: " + result)
}, 1000);
})(i);
}

Related

Return value for setTimeout function inside a for loop

Hi all when i am executing this piece of code i am getting 40 in my console log can some one plese explain me why
for (var i = 0; i < 10; i++) {
setTimeout(function () {
console.log(i);
}, 300000)
};
What you are getting on the console is the id returned by setTimeout(). If you run that code again you might get different value. We cannot predict this id, it is random.
If you want to print the values from 0-9 using setTimeout(), you need to take care of scope.
Using bind():
console.log('before loop');
for (var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(this.i);
}.bind({
i: i
}), 1000)
}
console.log('after loop');
Using closures:
console.log('before loop');
for (var i = 0; i < 10; i++) {
(function(i) {
setTimeout(function() {
console.log(i); //<-- parameter i
}, 1000);
})(i); //<-- i from loop scope
}
console.log('after loop');
Print in increasing order of timeout.
console.log('before loop');
for (var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(this.i);
}.bind({
i: i
}), 100 * i); //<-- note `* i`
}
console.log('after loop');
Your code will print 10 after 300.000 milliseconds.
Try setting it to a shorter period and you will see 10 printed.
What you probably see is linenumber in the console.
when I am executing this piece of code I am getting 40 in my console log
The reason is that setTimeout() moves the function from the execution queue, it will only be invoked after JavaScript has finished with the current execution queue.
Understanding JavaScript Timers

Will the ajax callback always be executed after the loop?

I have this Javascript/JQuery code:
$.ajax({...}).done(function() {
console.log('done');
});
for(var i = 0; i < 10; i++)
console.log(i);
Let's say the ajax request completes when the loop has reached the 5th iteration, will the browser suspend the loop execution and run the callback or will it wait for the loop to finish?
To make it more clear, is an output like this possible?
0
1
2
3
4
done
5
6
7
8
9
or will the output always be:
0
1
2
3
4
5
6
7
8
9
done
More in general I was wondering if the browser always pushes the callback, when triggered, at the bottom of the execution queue.
You could get both output example you describe, depending on the time the ajax request takes to complete.
EDIT: Note that this might lock your browser for a while, but it should not crash, and then prints something like ***** DONE! ***** 2828725
var promise = new $.Deferred(),
i = 1, j = 0, to = 5000000;
promise.done(function() {
document.write("***** DONE! *****\n" + i);
i = to;
});
setTimeout(function() {
promise.resolve();
}, 1000);
for(i = 1; i < to; ++i) {
console.log(++j);
//alert(++j);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
The test I did seems to show the loop must complete first
fiddle
time = new Date()
new Request.JSONP({
url: 'http://jsfiddle.net/echo/jsonp/',
onSuccess: function (response) {
console.log('done ' + (new Date()-time))
}
}).send();
for (var i = 0; i <= 65535; i++) {
for (var j = 0; j <= 10000; j++) {
if (i===65535 && j == 10000)
{
console.log('loop complete ' + (new Date()-time))
}
}
}

setTimeout() in cycle, strange behaviour

guys! I am a total newbie in javascript. I wrote my first program and already stuck with it's behavior:
var elements = document.getElementsByTagName("img");
for (var i = 0; i < elements.length; i++) {
if (i % 2 == 0) {
elements[i].src = "img.jpg";
for (var j = 0; j <= 1; j += 0.1) {
setTimeout(increase_opacity(elements[i], j), 2000);
// setTimeout(alert(j), 2000);
}
}
}
function increase_opacity(element, opacity) {
element.style.opacity = opacity;
}
I can not see that opacity changes, but under debugger it does, so setTimeout just doesn't work. If I uncomment // setTimeout(alert(j), 2000); I can see both opacity change and alert message on every cycle step. Why is that?
setTimeOut accepts a function, and what you were doing is passing it the result of a call to increase_opacity. Code below should work for you. Notice how increase_opacity is wrapped inside of the anonymous function definition.
var elements = document.getElementsByTagName("img");
for (var i = 0; i < elements.length; i++) {
if (i % 2 == 0) {
elements[i].src = "img.jpg";
for (var j = 0; j <= 1; j += 0.1) {
(function(element, opacity) {
setTimeout(function() { increase_opacity(element, opacity) } , 2000);
})(elements[i], j);
// setTimeout(alert(j), 2000);
}
}
}
Here is the doc on setTimeOut http://www.w3schools.com/jsref/met_win_settimeout.asp
You need to pass a function to setTimeout(). What you are doing is calling increase_opacity immediately and then passing the return value to setTimeout().
Since you want to call the function with particular arguments, you can use a closure to record the current values of i and j:
setTimeout((function(a, b) {
return function() {
increase_opacity(a, b);
};
})(elements[i], j), 2000);
Are you aware that setTimeout() is not a blocking call? Your script does not stop at the setTimeout() function, but continues its execution. So here's what's happening here:
1) Your code starts the loop;
2) When it enters a setTimeout(), it launches the timer it and goes ahead;
3) Repeat step 2 for every element; (Please note that steps 2 and 3 are executed in a few ms)
4) All the timers expires, all the instructions are executed all at once.
To avoid this, you should write something like:
var elements = document.getElementsByTagName("img");
for (var i = 0; i < elements.length; i++) {
if (i % 2 == 0) {
elements[i].src = "img.jpg";
setTimeout(function(){increase_opacity(elements[i],0);}, 2000);
}
}
function increase_opacity(element, opacity) {
element.style.opacity = opacity;
if (opacity < 1) setTimeout(function(){increase_opacity(element, opacity+0.1);},2000);
}
NB: When dealing with setTimeout() you should avoid that notation when calling function with parameters. You should always use this:
setTimeout(function(){yourFunctionName(arg1, arg2, arg3);});
NB 2: setTimeout(alert(j),2000); is not even waiting 2 seconds to appear. Like I said above, you should use the anonymous function when dealing with setTimeout(). It's probably giving the impression it's working because of this, but it's a sort of glitch IMO.

JavaScript async callback and scope

Consider the following example:
var cb = function (t) {
console.log('callback -->' + t);
};
for(var i = 0; i<3; i++) {
console.log(i);
setTimeout(function(){
cb(i);
},1000);
}
Working example at jsfiddle
The output of this code snippet is:
0
1
2
callback ---> 3
callback ---> 3
callback ---> 3
Everything works as expected, for loop puts 3 callback calls into the event loop. By the end of the for loop i == 3 and when the callbacks get executed all of them print 3 because they contain the link to the i which is 3.
How could this snippet be improved so when the callback gets executed it uses the actual value which was passed to it.
The output should be:
callback ---> 1
callback ---> 2
callback ---> 3
Thanks in advance.
Create a closure so that the setTimeout handler will refer to the closure's local variable (which in this case, we also named i), not the i from the loop:
for (var i = 0; i < 3; i++) {
(function (i) {
console.log(i);
setTimeout(function () {
cb(i);
}, 1000);
}(i));
}
You could try .bind:
for(var i = 0; i<3; i++) {
console.log(i);
setTimeout(cb.bind(null, i),1000);
}
The demo.
The traditional way to handle this is to create a closure:
for(var i = 0; i<3; i++) {
console.log(i);
setTimeout((function(i){return function(){cb(i)}}(i)),1000);
}
A frequent question.
Let's try to use some features of future of JS. I mean let.
It creates local scope variable and you don't need to use a closure or another trick.
But now it works only in FF (i'm using 20.0.1)
for(var i = 0; i<3; i++) {
console.log(i);
let a = i;
setTimeout(function(){
cb(a);
},1000);
}
I will just use setTimeout() in your cb's functoin
var cb = function (t) {
setTimeout(function(){
console.log('callback -->' + t);
},1000);
};
for(var i = 0; i<3; i++) {
console.log(i);
cb(i);
}

Can't call a function through setTimeout

I'm trying to make a few functions to work one after the other with a waiting time of 1.5 seconds between them.
NOW, when i try doing so with the same Id (Inside the "NoteList(>here<)", like 1, 2, 3, or any other, it works;
for (var i = 0; i < 36; i++)
{
setTimeout(function () { OnClcRandom(NoteList[0]) }, i * 1000 + 1000);
}
BUT! when i try doing so with the var i, it doesn't work and gets the all of the functions in the page stuck. any idea why?
for (var i = 0; i < 36; i++)
{
setTimeout(function () { OnClcRandom(NoteList[i]) }, i * 1000 + 1000);
}
That would be because all of the functions refer to the same live i variable, not the value of the variable at the time you called setTimeout(). Which means by the time the timeouts actually run your function i will be 36.
Try this instead:
for (var i = 0; i < 36; i++) {
(function(x){
setTimeout(function () { OnClcRandom(NoteList[x]) }, i * 1000 + 1000);
)(i);
}
This executes an anonymous function on each iteration of the loop, with each execution getting its own x parameter for use in your original function.
Javascript doesn't create local scope for block. :)
And in your second example var i equal 36 (last value).
You need create local scope inside loop.
for (var i = 0; i < 36; i++) {
(function (i) {
setTimeout(.......);
}(i))
}
You also may fixed 'i' value assign it to function property:
for (var i = 0, f; i < 36; i++){
f = function _callback() { var i = _callback.i; .....};
f.i = i;
setTimeout(f, i * 1000);
}

Categories