denoMain. Only after it has finished running this function does Deno starting its event loop. Once the event loop stops, the Deno process dies.
denoMainis located in
libdeno, which is the exposed API from the middle-end, that whenever there is a message sent from the Rust side, please forward the buffer to a function called
handleAsyncMsgFromRust. Then, a
Startmessage is sent to Rust side, signaling that we are starting and receiving information including the current working directory, or
cwd. We then decide whether the user is running a script, or using the REPL. If we have an input file name, Deno would then try to let the
runner, which contains the TypeScript
compiler, to try running the file (going deep into its definition, you'll eventually find a secret
denoMainonly exits when the
runnerfinish running the file.
denoMainrunning to its end, a set of asynchronous calls might have been made without any of them being responded. Deno would then call the
isolate.event_loop()to start the loop.
event_loopis located in
self.is_idle(). If yes, we will wait for
self.rx, which is the receiving end of a message channel, to receive some message, or wait for a timer, created by
setTimeout, to expire.
self.rxreceived some message, this means that one of the task in the Tokio thread pool has completed its execution. We would then process its execution result, which is a pair of
req_id(the task id) and
buf(the buffer containing serialized response), by sending them to
self.complete_op, which forwards the result based on request id and invoke the code that is waiting for this output, running to the end. Notice that the code invoked here might schedule for more async operation.
setTimeoutwould similarly be invoked and running to the end.
true. This happens when there are no more pending tasks. Then, the event loop exits and Deno quits.
req_idis provided to
self.complete_opas a task id. Actually, it is used to retrieve the correct promise created by the past async operation from a promise table, which holds all the promises that are pending resolve.
dispatch.sendAsync, which is used to forward async requests to Rust.
cmdIdhere is essentially the
req_idon the Rust side. It is returned by
sendInternal, which calls into V8 Send binding and increments a global request id counter. For each async operation, we create a deferred/resolvable and place it into the promise table with
cmdIdas the key.
denoMainsection, we saw that we attach
libdenoas the receive callback whenever a new message arrives. Here is its source code:
cmdIdfrom the response and find the corresponding Promise in the table. The promise is then resolved, allowing code awaiting the response to be executed as microtasks.