2

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.

5 Answers 5

1

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);
Sign up to request clarification or add additional context in comments.

2 Comments

Besides setTimeout and setInterval, can I consider all else synchronous code?
no, all ajax calls are asynchronous as well. In nodejs nearly half of the functions are asynchronous :)
1

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.

http://latentflip.com/loupe/

1 Comment

Thanks James, this video is very useful!
1

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:

  1. Your for loop goes into the queue and start running
  2. The SetTimeout function is added into the queue after the specified time, in your case after 0 miliseconds.
  3. Once the thread is free from the for loop, the SetTimeouts are removed from queue and executed.

Here are some links which may be helpful

http://ejohn.org/blog/how-javascript-timers-work/

http://javascript.info/tutorial/settimeout-setinterval

Comments

0

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);
}

1 Comment

I know the correct code. I'm just trying to understand the execution order.
0

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.

enter image description here

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.