Execution contexts
Now it's time to look at the JavaScript interpreter itself—the component that takes over from other browser components when events take place and code needs to run. There's always an active JavaScript context, and within the interpreter, we'll find a stack of contexts. This is similar to many programming languages where stacks control the active context.
Think of the active context as a snapshot of what's happening right now in our JavaScript code. A stack structure is used because the active context can change to something else, such as when a function is called. When this happens, a new snapshot is pushed onto the stack, becoming the active context. When it's done running, it's popped from the stack, leaving the next context as the active context.
In this section, we'll take a look at how the JavaScript interpreter handles context switching, and the internal job queue that manages the context stack.
Maintaining execution state
The stack of contexts within the JavaScript interpreter isn't a static structure—it's constantly changing. There's two important things that happen throughout the lifetime of this stack. First, at the top of the stack, we have the active context. This is the code that currently executes as the interpreter moves through its instructions. Here's an idea of what a JavaScript execution context stack looks like with the active context always at the top:
The other important responsibility of the call stack is to bookmark the state of an active context when it's deactivated. For example, let's say that after a few statements, func1()
calls func2()
. At this point, the context is bookmarked to the spot directly after the call to func2()
. Then, it's replaced with the new active context—func2()
. When it completes, the process is repeated and func1()
again becomes the active context.
This kind of context switching happens all over our code. For example, there's a global context, which is the entry point into our code, there's the functions themselves which have their own context. There are also more recent additions to the language, which have their own contexts, such as modules and generators. Next, we'll look at the job queues responsible for creating new execution contexts.
Job queues
Jobs queues are similar to the task queues that we looked at earlier. The difference is that job queues are specific to the JavaScript interpreter. That is, they're encapsulated within the interpreter—the browser doesn't interact directly with these queues. However, when the interpreter is invoked by the browser, in response to a loaded script or event callback task for example, new jobs are created by the interpreter.
The job queues within the JavaScript interpreter are actually much more straightforward than the task queues that are used to coordinate all the web browser components. There are only two essential queues. One is for creating new execution context stacks (call stacks). The other is specific to promise resolution callback functions.
Note
We'll go into more depth on how the promise resolution callback job works in the next chapter.
Given the restricted responsibilities of these internal JavaScript job queues, one might draw the conclusion that they're unnecessary—an act of over engineering. That's not true, because while today there's limited responsibilities found in these jobs, the job queue design allows for much easier expansion and refinement of the language. In particular, the job queue mechanism is favorable when considering new concurrency constructs in future versions of the language.