6.7 Evaluate module

Overview

Module evaluation is akin to executing a program. Deno triggers v8's module evaluation function, prompting V8 to swiftly assess the module's content. Remember, this evaluation process occurs solely on the main module, similar to how the primary program runs in languages like C, C++, or Java.

Evaluation of hello v2

Below is the important evaluation code from the worker:
pub async fn execute_main_module_possibly_with_npm(
&mut self,
) -> Result<(), AnyError> {
let id = self.worker.preload_main_module(&self.main_module).await?;
self.evaluate_module_possibly_with_npm(id).await
}
The result of the preload_main_module function is modules that are created with a return value that holds the ID of the main module. After this, the next task is to assess the main module. The preload module provides the ID of the main module, which is used in the module evaluation process.
Sure, here's the rewritten version of the subsection with corrected grammar:
As a reminder, mod_evaluate carries out two actions:
  1. 1.
    It invokes mod_evaluate.
  2. 2.
    This triggers v8's evaluate function.
  3. 3.
    It establishes a global promise that will either be fulfilled or rejected at some point in the future.
  4. 4.
    It waits asynchronously in run_event_loop/poll_event_loop until the global promise transitions from pending to fulfilled status.
pub async fn evaluate_module(
&mut self,
id: ModuleId,
) -> Result<(), AnyError> {
self.wait_for_inspector_session();
let mut receiver = self.js_runtime.mod_evaluate(id);
tokio::select! {
// Not using biased mode leads to non-determinism for relatively simple
// programs.
biased;
maybe_result = &mut receiver => {
debug!("received module evaluate {:#?}", maybe_result);
maybe_result.expect("Module evaluation result not provided.")
}
event_loop_result = self.run_event_loop(false) => {
event_loop_result?;
let maybe_result = receiver.await;
maybe_result.expect("Module evaluation result not provided.")
}
}
}
In the previous chapter, the basic hello world program didn't contain any asynchronous operations. As a result, the poll event loop inside mod_evaluate completed immediately, since the global promise transitioned to a fulfilled state. After the module evaluation finished, the program progressed through window load, the event loop, window unload, and then concluded.
However, in this scenario, we have both asynchronous and synchronous operations:
  • Console logs use synchronous operations.
  • getMachineId employs an asynchronous operation.
  • nanoid relies on synchronous operations.
Although there's a combination of operations, they won't consume much time, as they are minimal and straightforward. The only exception arises when a setTimeout function is present, which would introduce a delay in module evaluation, causing a pause. To demonstrate this, let's adjust our code by incorporating the setTimeout function. The updated app code is as follows:
import { nanoid } from "npm:nanoid";
import { getMachineId } from "https://deno.land/x/machine_id/mod.ts";
const id = nanoid();
const machineId = await getMachineId();
const homeDir = Deno.env.get("HOME");
function printNumber(input: number) {
console.log(input);
}
function printString(input: string) {
console.log(input);
}
printNumber(1);
printString("One");
console.log("Nanoid=", id, ", MachineId=", machineId, ", homeDir=", homeDir);
setTimeout(() => {
console.log("Timeout occured");
}, 10000);
Once all the ops are completed, the program will print:
1
One
Nanoid= iDNVv3wRy9w3zNDplgb_h , MachineId= 2D809C23-917A-5052-BB4F-38794CCCE9FA , homeDir= /Users/mayankc
Timeout occured
As we discussed before, we will now cover the final two operations to demonstrate their implementation in Deno:
  1. 1.
    nanoid
  2. 2.
    console.log
By understanding these, we will grasp the process of crossing the bridge, executing ops, and handling the outcomes. Moving forward, we will delve into the mechanics of a synchronous op in the upcoming section.

Event loop

Once you've sent the main module for evaluation, the event loop starts. During this time, the loop continues until the following conditions are met:
  • There are no pending operations.
  • There are no pending dynamic imports.
  • All pending dynamic imports have been evaluated.
  • All pending top-level modules have been evaluated.
When the evaluation of the main module is finished, all operations will also be completed. However, due to a timer set at the end, the program will keep running until the timer runs out. Once this happens, the program will end because there are no more tasks to perform. The remaining steps, such as window loading, the event loop, and window unloading, will follow the same pattern as the "hello world" program.
--
The final part of this book will cover the internal workings of synchronous operations (sync ops). With our existing knowledge of sync ops, we are prepared to deepen our understanding in the upcoming section.