19

I've read several posts/SO threads on event loop, and according to MDN's article,

When the stack is empty, a message is taken out of the queue and processed.

As a JS novice, what I'm still confused about is -- when exactly does the call stack become "empty"? For example,

<script>
function f() {
  console.log("foo");
  setTimeout(g, 0);
  console.log("foo again");
}
function g() {
  console.log("bar");
}
function b() {
  console.log("bye");
}

f();
/*<---- Is the stack empty here? */
b();
</script>

The correct order of execution is foo - foo again - bye - bar.

But today I started thinking: isn't the stack technically empty right after exiting the f() call? I mean at that point we're not inside any function, and we haven't started any new execution, so shouldn't the setTimeout call message (which has been immediately queued) be processed, before moving on to b(), and giving the order of foo - foo again - bar - bye?

What if we have a million lines of code or some intensive computation to be executed and the setTimeout(func, 0) just sits in the queue for however long?

Yibo Yang
  • 2,353
  • 4
  • 27
  • 40
  • The best way I can think to explain it is that the call stack isn't empty until the code finishes running all relevant paths. A setTimeout of 0 simply pushes your code to the end of the stack, IE: after b(). – Jacques ジャック Jan 10 '16 at 04:02
  • There's still `b` to execute so the stack isn't empty after `f`. What else is there to do after `b`? – MinusFour Jan 10 '16 at 04:08
  • 1
    @Jacques I thought the concept of stack is to be understood with regard to function calls/frames rather than if there's still code left to be executed... I got that impression from the [MDN example](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#Stack) and [this demo](http://latentflip.com/loupe/)... – Yibo Yang Jan 10 '16 at 04:08
  • 1
    This video is very helpful on the topic: https://vimeo.com/96425312 In short the timeout event won't happen until after the remaining script executes. – stevecass Jan 10 '16 at 04:09
  • Since the global environment is an execution environment, I suppose that is at the bottom of the call stack when the script initially loads. –  Jan 10 '16 at 04:11
  • @YiboYang The example you referenced ends with the same method that calls all other methods. – Jacques ジャック Jan 10 '16 at 04:12
  • @YiboYang Have any of these answers helped? If so, please upvote and/or mark accepted. – Jacques ジャック Jan 10 '16 at 04:23
  • As long as there is still some code running the call stack will not be empty and the opposite statement is also true. As long as the stack is not empty there is still code running. – Tesseract Jan 10 '16 at 04:24

4 Answers4

10

Although the block of code within the <script> tags isn't wrapped in an explicit function, it can be helpful to think of it as being a global function that the browser tells the javascript runtime to execute. So the call stack isn't empty until the code in the script block is done executing.

RJM
  • 1,164
  • 9
  • 21
4

When the current piece of Javascript that is executing has finished and has no more sequential instructions to execute, then and only then will the JS engine pull the next item out of the event queue.

So, in your example:

f();
b();
// JS is done executing here so this is where the next item will be
// pulled from the event queue to execute it

Javascript is single-threaded which means the current thread of Javascript runs to completion, executing all instructions within a sequence until it hits the end of the code. Then, and only then, does it pull the next item from the event queue.

Here are some other answers that may help you understand:

How Javascript Timers Work

How does JavaScript handle AJAX responses in the background? (a whole bunch of Event Loop references in this post)

Do I need to be concerned with race conditions with asynchronous Javascript?

Can JS event handlers interrupt execution of another handler?

Community
  • 1
  • 1
jfriend00
  • 683,504
  • 96
  • 985
  • 979
2

The best way I can think to explain it is that the call stack isn't empty until the code finishes running all relevant paths. A setTimeout of 0 simply pushes your code to the end of the stack.

When code runs at runtime, everything that will be run is part of the call stack, the order will be adjusted based on the order things are called and any timeouts/intervals/async methods that are called.

Some examples:

function foo() {
  console.log('foo');
}

function bar() {
  baz();
  console.log('bar');
}

function baz() {
  setTimeout(function() { console.log('timeout') }, 0);
  console.log('baz');
}

foo();
baz();
// call stack ends here, so, timeout is logged last.

// in console
// foo
// baz
// timeout

As you can see, bar is not included in the runtime stack because it is not called. If we have some HTML:

<div onclick="bar()">Bar runs</div>

When you click on that div, you will see baz, bar, then timeout logged to the console because the timeout is always pushed to the end of the currently running processes/call stack.

Hope this explanation helps!

Jacques ジャック
  • 3,682
  • 2
  • 20
  • 43
  • Thanks -- so in my example, what exactly **is** still on the stack after `f()` finishes? – Yibo Yang Jan 10 '16 at 04:26
  • b() and any timeouts that have not run. (Which, in your example would be all of them.) Think of it this way, every statement that will run is in your call stack, in the order they are called in your code. The only time they are not in that order is in the case of timeouts/intervals/async methods. – Jacques ジャック Jan 10 '16 at 04:29
  • 1
    "A setTimeout of 0 simply pushes your code to the end of the stack" - It is more accurate to say it is pushed to an exclusion *queue*, not to the call stack - these are two different concepts (jfriend00's answer explains that nicely). – Kobi Jan 10 '16 at 09:01
  • @Kobi I'd rather not argue a semantic point. what you're calling a queue *is* the stack, the stack *is* a queue of statements that need to run. – Jacques ジャック Jan 10 '16 at 13:18
  • @Kobi's point is valid, and it's not incorrect. There's a big difference between the event queue and the call stack - namely, the event queue is FIFO and a stack is LIFO – Andrew Templeton Jan 10 '16 at 21:26
  • @AndrewTempleton Haha, now we're getting even more into semantics, but I get what you're saying ----- The stack is only LIFO because methods *can* call another method that calls another, etc, so the original method doesn't finish until the rest. (Doesn't have to be that way, but almost always is) The event queue basically lists all those methods in the order they will finish. Technically, the OPs question has to do with the Event Queue, which is essentially the call stack in most cases. Regardless of how you word it, the last thing to happen in the queue or stack is the timeout. – Jacques ジャック Jan 10 '16 at 21:39
  • To clarify, I see them is almost the same thing, you may be view them differently, but we both seem to know what we're talking about, which is why I think it's just semantics. – Jacques ジャック Jan 10 '16 at 21:40
2

The simplest possible explanation: when all synchronous code in the current script, function, or event handler has finished running.

To directly answer "what if I have millions of lines..." Yes - your setTimeout call is stuck in the queue and will wait for its turn.

Andrew Templeton
  • 1,656
  • 10
  • 13