I've recently seen a presentation on the JS event loop which is, frankly, brilliant, but I have a lingering question now about the JS call stack. If you think about the global execution context as, say, main(), is main() never resolved? My reasoning here is that, if it were, then the JS program would be complete, and no callbacks would happen.
My primary interest here is how the call stack is represented, in relation to the callback queue. If the event loop is said to wait until the call stack is empty before pushing new frames onto the stack, then the loop would be waiting until the program is complete, and callbacks wouldn't have any effect.
I suppose that means that the event loop waits until there is only one frame left (the main execution context), not no frames.
As you said, main() is the global execution context. It's still alive until :
So unless one of the use cases above happen, the runtime is still alive, so is the event loop (which will process the task queue).
If you think about the global execution context as, say, main()
main is not the global execution context, it is the code that initially runs in that context. The global environment is - through closure - still preserved for the outstanding callbacks after your global code ran, it will not get garbage-collected until your program has finished.
is main() never resolved?
Of course it is.
main() is the invocation of any global code - basically, when you loaded a script in your webpage and it executes (yes, this can happen multiple times). Or the global program that you tell node to execute. And once that code has finished running, it does indeed get popped of the stack, as you can see in the talks's animation very well. It does not "continue to run", as that would block the event loop.
My reasoning here is that, if it were, then the JS program would be complete, and no callbacks would happen.
No. The script might have been completed, yes, but your program (browser environment, node etc) has not yet. The event loop is still spinning (or, to be exact: just starts spinning).
The event loop will look at the ongoing asynchronous tasks (XHRs, timeouts, file IO etc) and notice there still are some things working. The program does not exit. Once one (or multiple) of those tasks finish, they put their callback in the callback queue, where it gets picked up by the event loop, and processed by executing it on a brand new call stack. When that stack is empty again, the event loop is back in control, and picks up callbacks from the queue until it is empty. Then again, it looks whether there are still ongoing tasks (one of the callbacks might have spawned new ones), and only when there are none, then the program really ends, and the global environment can be trashed.
©2020 All rights reserved.