Reputation: 28572
I have a piece of Javascript code.
var i, _i, _len, _ref;
_ref = [1, 2, 3];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
i = _ref[_i];
setTimeout((function() {
return console.log(i);
}), 0);
}
It's generated by a piece of Coffeescript and that's why the weird names in it. When you execute the code, the output would be 3 3 3
. Based on the execution result, it seems that the execution order is:
enter for loop
settimeout 1 with a function
settimeout 2 with a function
settimeout 3 with a function
settimeout 1 execute
settimeout 2 execute
settimeout 3 execute
It's a little different with my understanding. As I understand, the setTimeout
in the for loop runs asynchronously. The setTimeout
statement itself can be completed instantaneously. After some milliseconds (0 in our case), the function will start to execute in a separate context. So my expected execution order is:
enter for loop
settimeout 1 with a function
settimeout 2 with a function, settimeout 1 execute
settimeout 3 with a function, settimeout 2 execute
settimeout 3 execute
Based on the CPU load and the complexity of the function passed to setTimeout
, the execution order may be different with what I described above. But the my point is, setTimeout could start executing right away and don't need to wait the for loop's completion.
Hope someone can help to clarify. I check MDN for setTimeout but can't get anything useful.
Upvotes: 2
Views: 9549
Reputation: 3307
I have here simplefied the loop, you can see the result here: https://jsfiddle.net/TomKarachristos/L6couu2o/
for ( _i = 0, _len = 3; _i < _len; _i++) {
setTimeout((function() {
makeAParagraph(_i);
}), 0);
}
When a piece of code is execute, nothing can interrupt the execution, so all the loop is execute without a single timeout to run.
In that execution every time a timeout found it push the first parameter(a function) inside in a callback queue. As the picture below.
When loops end and there is no code to execute JavaScript go to callback queue find the first function and execute it. If this is a timeout first is checking if the time you have put as second parameter have pass(0 is always pass).
So the first timeout it's run when the loop ends, and when this happen the i is 3. Here we have another technique of JavaScript named closures, function in JavaScript can access variables defined outside the function. So the function inside the timeout can access the variable i outside. But when the first timeout is called the i is 3, as you see in the picture, the same with the second and third timeout.
Upvotes: 0
Reputation: 24541
The best way for me to expain that was changing the understanding of what is any asynchronous call in JavaScript, and setTimeout
in particular. Instead of thinking of setTimeout
as of direct function call you may consider it as adding some event to the queue which will start running all events only after everything else is done.
After you think that this is just adding the event, you understand how it naturally works in JavaScript. JavaScript applications are one-thread applications (most of the time). So that means that as long as your synchronous code is working it won't even start to run asynchronous requests, so won't even touch the queue.
In other, more simple words, setTimeout(..., 0)
is adding some kind of 'onSyncCodeDone' event.
The only way how you can get your expected execution order is using recursion:
var _ref = [1, 2, 3];
function process(index) {
if (index < _ref.length) {
setTimeout((function() {
process(index + 1);
return console.log(index);
}), 0);
}
}
process(0);
Upvotes: 1
Reputation: 906
I will try to be as simple as possible. JavaScript is a single threaded language, which means that only single operations will take place at an instance of time.
settimeout 2 with a function, settimeout 1 execute
the execution order which you wrote shown above is not possible, The two statements will not be executed at the same time. There is an execution queue which executes statements one by one in JavaScript The order in which it works as per your code written above goes like this:
Here are some links which may be helpful
http://ejohn.org/blog/how-javascript-timers-work/
http://javascript.info/tutorial/settimeout-setinterval
Upvotes: 1
Reputation: 11807
There is an idea of an event loop. Even though the setTimeout is set to 0 (it really can't go lower than around 14ms), it still gets placed in the event loop. So, you'd expect it to fire instaneously, but it might not depending what else happens outside the context, for instance, that loop running.
check this site out, it will blow your mind, and exactly what you need to understand.
Upvotes: 1
Reputation: 2234
The problem is setTimeout will be executed after the loop executes. because that is not synchronous. So the function inside setTimeout will take the value of i as 3 all time.
This will be the correct code...
var i, _i, _len, _ref;
_ref = [1, 2, 3];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
i = _ref[_i];
setTimeout((function(i) {
console.log(i);
})(i), 0);
}
Upvotes: 0