5.17 Evaluate module
This is the moment we've been eagerly anticipating! It's the very reason behind all the steps we've taken. Now, it's the perfect time to set our code in motion. In the realm of v8, making a module come to life is referred to as module evaluation. Let's delve into understanding how our simple "hello world" program goes through this evaluation process, essentially getting executed and bringing our code to life.
Overview
Once all the necessary modules have been fetched, cached, loaded, transpiled, compiled, and prepared for use, the next step is to initiate the execution of the application. Similar to how a C, C++, or Java program begins with the main program, a JavaScript code commences its execution from the main module or what is often referred to as the root module.
Immediately after the loading process is completed, the worker's run function triggers the evaluate_module function. This function is responsible for initiating the evaluation or execution of the root module. To achieve this, the evaluate_module function requires the module ID of the root module as its input.
The evaluate_module function undertakes two crucial tasks:
It invokes the mod_evaluate function of the JavaScript runtime, thereby initiating the module evaluation within the V8 engine.
It facilitates the operation of the well-known event loop, ensuring that the system awaits the completion of the module evaluation process and its resultant output. This step is fundamental for coordinating asynchronous operations and managing the flow of the program.
The mod_evaluate function initiates the execution process within the V8 engine. After this, the program enters an event loop phase that continually checks for various outcomes such as evaluation results, resolutions of promises, operational (OP) results, handling of dynamic imports, and identification of errors. To delve into the topic further, let's begin by exploring the intricacies of module evaluation. Subsequently, we'll delve into the functioning of the event loop, which plays a crucial role in managing these asynchronous tasks and their outcomes.
Module evaluation
The initial module evaluation function is called mod_evaluate and it goes through these steps:
It sends the main module for evaluation.
It looks out for any instant:
Errors
Rejections of Promises
It's important to note that not all errors are brought up as soon as the evaluation begins. Nonetheless, certain errors are indeed raised right away in the process.
The function called mod_evaluate is somewhat larger. The next illustration demonstrates the sequence of actions that this function carries out:
Here is the source of mod_evaluate():
Below is an expanded and grammatically corrected version of the provided text, along with additional information on the topic:
Let's delve into the intricacies of the process, breaking it down into distinct steps:
Obtaining the Module Handle: To initiate the process, the first step involves acquiring the module handle using the assigned module identifier. This handle serves as a way to interact with the specific module under consideration.
Invoking v8's Module Evaluate Function: The subsequent action entails invoking the module evaluate function provided by v8. This function plays a pivotal role in evaluating and processing the module's contents.
Verifying Immediate Evaluation Errors: After the evaluation function is called, a crucial check is performed to identify any immediate errors that might have arisen during the evaluation process. This preliminary assessment ensures that the module is evaluated correctly and any errors are promptly identified.
Fetching a Global Promise for Module's Scope: As the evaluation progresses, a global promise is acquired to establish the scope of the current module. This promise encapsulates the asynchronous execution and encapsulation of the module's functionality.
Storing the Global Promise: The acquired global promise is then stored within a designated container known as "pending_mod_evaluate." This repository serves as a means to manage and keep track of promises associated with module evaluations.
Providing a Return Path for Async Events: Finally, to ensure a smooth flow of asynchronous events and facilitate the retrieval of asynchronous updates later, the process concludes by returning the receiver. This return path acts as a means to capture and process any events that may occur asynchronously during the module's evaluation.
After submitting the module for evaluation, it's time to delve into the renowned event loop.
Event loop
Once you've sent the module for assessment, the JavaScript runtime steps into an asynchronous event loop. Think of this event loop as a busy worker that's responsible for managing different tasks. One of its important tasks is to keep an eye on the main promise, which is like a commitment made when you first introduced your code for evaluation.
This event loop is made up of various components, each with its own role, but delving into all the details right now might be a bit overwhelming. For our current focus, which is the simple "hello world" program, there are a couple of parts within the event loop that matter:
Checking pending module evaluation: This involves making sure that the module you submitted is being properly evaluated. The event loop ensures that the evaluation process is happening smoothly and without any hiccups. It's like a supervisor making sure the work is being done.
Checking promise rejections: In the world of programming, promises are used to represent actions that might take some time to complete. If something goes wrong during these actions, promises can be rejected. The event loop keeps a watchful eye on these promises to catch any rejections and handle them appropriately. It's like having someone who catches errors and helps fix them.
So, you can think of the event loop as a diligent manager overseeing your code's progress, making sure evaluations are happening correctly and problems are taken care of. It's like the backstage crew of a show, ensuring everything runs smoothly for the main performance.
The aspect of the event loop's functioning that involves other tasks remains mysterious at this point. This is primarily due to the simplicity of our current example. In this scenario, there are no tasks that involve asynchronous calls, intricate dynamic imports, operations (referred to as "ops"), event listeners, and similar complexities. As a result, the hello world program commences and concludes on its own. It doesn't rely on any external dependencies to maintain its execution.
The global promise that is linked with the main module encapsulates the outcome of evaluating the hello world program. It's worth highlighting that the program's outcome isn't equivalent to its actual output. The visible output of the program is directed to a distinct location. Conversely, the assessment's outcome is fed back into the event loop via the global promise.
The following is the code of a single tick of the event loop:
In the Deno runtime, during each cycle of the event loop, numerous tasks are carried out. As we discussed earlier, in the context of the simple "hello world" program, the crucial aspect to note is the execution of the function call known as "evaluate_pending_module."
When a module is being evaluated, there's a possibility of encountering errors. These evaluation errors manifest asynchronously, typically as promise rejections. These errors adhere to the standard JavaScript error types, such as ReferenceError or TypeError, among others.
To manage this, in every iteration of the event loop, the global promise pool is examined until it transitions from a pending state. This transition might involve moving to either a resolved or a rejected state, depending on the outcome of the asynchronous operations associated with the module evaluations.
Here is the code snippet responsible for the process of monitoring promises linked to pending evaluations:
Certainly, I'd be happy to help you rephrase and expand upon that subsection about Deno's global promises:
Within Deno, the global promise goes through three distinct states:
Pending
At this stage, the promise is in a waiting state.
It's like a question that hasn't been answered yet.
We'll come back and see if there's an answer in the next cycle.
Fulfilled
When a promise reaches this state, it means the task assigned to it has been successfully completed.
Think of it as getting a response to your question, and that response is positive.
If you were waiting for some information, now you have it, and you can share it with others.
Rejected
Unfortunately, not all promises end up fulfilled. Some end up in the rejected state.
This is like getting an answer, but it's not the answer you wanted. Something went wrong.
Just as sometimes things don't go as planned, the task associated with the promise couldn't be accomplished successfully.
In this case, you get an error message instead of the expected result.
After the global promise has been either fulfilled or rejected, the evaluation of the module finishes. The event loop comes to an end when the following conditions are satisfied:
There are no pending operations left.
There are no pending dynamic imports.
There are no dynamic imports awaiting evaluation.
There are no top-level modules waiting to be evaluated.
This signifies that the entire process of evaluating the module and handling asynchronous operations has been completed. The event loop, which is responsible for managing the execution flow, can gracefully conclude its operations once all these requirements are met.
In brief, when all tasks have been completed, the event loop concludes its operations, leading to the program's termination. In essence, if no further tasks remain, both the event loop and the program itself come to an end.
Evaluation of hello world
Let's take a closer look at how the evaluation process functions in our uncomplicated "hello world" illustration. Just to refresh our memory, here's the JavaScript code we're working with:
The current example we're looking at involves a situation where no external modules are being imported. In this case, we're dealing with a singular module located at the root or main level. This main module takes center stage for evaluation; no other modules are considered. Additionally, there are no asynchronous function calls or any other activities that would cause it to continue running.
Let's delve into the sequence of events when the process of evaluating our main module, which we'll refer to as "mod_evaluate," takes place. This process unfolds as follows:
The V8 engine, which powers Deno, initiates the execution of the main module.
Within the V8 engine, the instruction to run
console.log
is executed.V8, in turn, triggers an external reference named "op_print" to display the number 1 on the standard output (stdout).
Moving on, V8 performs another
console.log
operation.Once again, V8 employs the external reference "op_print," this time to exhibit the word "One" on the standard output.
As the program lacks any further instructions, it reaches its conclusion.
The evaluation of the module by V8 comes to an end.
V8 designates the global promise of the module as fulfilled, indicating successful completion.
Given that there are no pending tasks or activities, the subsequent tick of the event loop transpires.
The global promise, having met its fulfillment, wraps up.
Consequently, the asynchronous polling concludes, culminating in the completion of the module's execution.
This step-by-step breakdown illustrates how the main module's journey unfolds during the evaluation process within the Deno environment.
Output:
After the module's execution has been completed, there remain additional tasks that the worker must attend to. Once the execute_main_module
function wraps up its operations, it proceeds to the subsequent stages within the implementation of the run command:
If there are any listeners, the window load event is sent out.
The program enters the event loop.
However, it's important to note that the main module has already been executed.
The event loop closes immediately because there are no other tasks to perform or wait for.
The window unload event is sent out.
This marks the completion of the program.
Deno then shuts down.
Here's what remains to be done:
The tasks that are left will be completed right away since the primary module has already been executed. The program will come to an end once these four functions are finished. Even the process of running the event loop will conclude promptly, as there are no more tasks remaining.
--
That's fantastic! We have successfully run our inaugural program.
Last updated