3

What's happening with that piece of code

var start = new Date();

setTimeout(function() {
    var end = new Date();
    console.log('Time elapsed with func1:',end - start, 'ms');
}, 500);

setTimeout(function() {
    var end = new Date();
    console.log('Time elapsed with func2:',end - start, 'ms');
}, 250);

while (new Date() - start < 1000) {}

Logs:

Time elapsed with func2: 1001 ms
Time elapsed with func1: 1017 ms

I was expected that func1 will be fire first because it's the first event to be added into the event queue. Then, due to the single thread nature of JS, wait until func1 returns and then executes the next event in the queue which is func2.
So, what's happening?

2
  • 2
    500ms > 250ms ... so, func2 will fire about 250ms before func1 - your tight while loop screws that pooch though - however, func2 will be added to the event queue before func1, because no matter what happens, 250ms comes before 500ms Commented Jan 9, 2016 at 8:41
  • Thanks for your answer. Commented Jan 9, 2016 at 8:48

3 Answers 3

9

First off setTimeout() is asynchronous and non-blocking. That means when you call it, all it does is register the new timer and then immediately returns. So, when your code runs, it will follow these steps:

  1. You register the func1 timer for 500ms.
  2. You register the func2 timer for 250ms.
  3. Each of the timers is added to a sorted data structure inside the timer portion of the event loop so it's easy for the event loop to know and check which timer is next to execute.
  4. You start the while spin loop. That loop runs for about 1000ms.
  5. When the while spin loop finishes, the Javascript interpreter gets to go back to the event loop to check if there is anything else to do. One of the things it checks (one of the phases of the event loop) is to see if there are any timers ready to run. It checks the head of the timer list and sees that its time has already passed by (meaning it is past time for it to run). So, it grabs the callback associated with that func2 timer and executes it. The func2 timer is at the start of the list because it's has the earliest time associated with it.
  6. When that callback finishes executing, the JS interpreter again returns control back to the event loop where it again checks if there isn't anything else to do. It will find that the func1 timer is now at the head of the list and past its time to run so it grabs the callback associated with that timer and executes it.

And thus, you get the output you see with the func2 callback executing as soon as the while spin loop is done, then the func1 callback executing after that.

Note, these timers are purely event driven. There is no interrupting of the currently executing Javascript to execute a timer. For this reason, timers in Javascript are a best effort kind of implementation. If you set a timer for 1000ms from now, then the callback associated with that timer will run no sooner than 1000ms and could be later than that if the JS interpreter was busy at the appointed time for that timer.

The one aspect of timers that is missing from the above description (because it doesn't occur in your situation) is what happens if you don't have the while() spin loop. In that case, your code returns control back to the event loop right after configuring both timers, the event loop checks to see if any timers are ready to run, but none are yet ready to run. It will then sleep until something wakes it up again, but not sleep longer than the time to the next currently set timer.

Sign up to request clarification or add additional context in comments.

4 Comments

After 250ms, func2 timer fires (internal to the JS engine) => Does this mean after 250ms, an event or an interruption will internally raise to the JS engine and JS engine will handle it immediately?
@Lane - No. Actually all that happens is the next time the event loop gets to the timer part of the loop, it checks to see if the next timer (they are stored in a sorted way) has reached its time. Timers in nodejs or the browser are completely handled in the event loop. There is no interrupt to them. The interpreter has to get back to the event loop before it can be checked to see if it's time for the next timer. I will modify this older answer to clarify how this works.
If I run code like this: setTimeout(print 1, 10ms); heavyTask(1000ms); setTimeout(print 2, 0ms);, the result of browser(Chrome) and Node.js varies. Chrome => 2 1. Node.js => 1 2. they are stored in a sorted way => this can explain Chrome behavior, but why Node.js is different? Real code please see here: gist.github.com/zhanglintc/4f800060dcf7dc0b6852657914760223
@Lane - I added comments to your recent question.
4

No, wait. It won't add your timeout call to the event queue. It's handled by the webapi by the browser and then when your timeout kicks-off then it'll add your function to the event queue.

Whatch this: https://www.youtube.com/watch?v=8aGhZQkoFbQ#t=13m (From 13:00)

Comments

1

Javascript is single-threaded. Even though there are asynchronous callbacks, they are not con-current.

This means, the code inside(setTimeout(fn, num)) only gets called once the entire code has finished execution. Therefore, if num parameter = 5000. This means, the function setTimeout(...) will run: after the entire code (i.e. not just the code up to the point when setTimeout is called but everything) has finished executing + 5 seconds. Hope this helps.

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.