Building a Node.js Event Loop Visualizer
Try the Live demo here!
While reading how Event Loop works for JavaScript & Node.js, I’ve come across this great explanation of JavaScript’s Event Loop by Philip Roberts. He had made a great visualizer called Loupe. However, I couldn’t find any similar visualizer that supports Node.js API like setImmediate()
or process.nextTick()
.
So why not build one, I thought to myself.
Overall Architecture
Not to reinvent the wheel, The project is based on the closest visualizer I could find, js-visualizer-9000. There are two components to this project, frontend and backend. The frontend is responsible for submitting code
and replaying the list of event
from the backend. The frontend is pretty much complete thanks to Dillion's work. We'll be focusing on the backend side of the project. The core of the application is how we're instrumenting the execution of code.
The flow of the program can be seen from the following diagram.
There are several steps required after we get the code
from the frontend.
- Create New Worker using Node.js
Worker threads
API
- This allows running the submitted
code
in an independent execution thread, preventing malicious code from crashing the server.
2. Transform Code & Inject Tracer
using Babel
- We’ll inject
Tracer
to instrument function call & prevent infinite loop.
3. Run Transformed Code using vm
module and async-hooks
enabled
The
vm
module enables compiling and running of the transformed code.async-hooks
is enabled to instrument flow of asynchronous function, read more here
4. Listen to Events Broadcast from worker
5. Collect & Reduce Events
- Reduce the broadcasted events into a consistent format for the frontend to replay the events
Instrumenting Execution of Code
We’re using two tools to instrument the execution flow of code:
Tracer Function
The purpose of Tracer
is to broadcast event
as it occurs. The event
is then used by the frontend to replay and visualize the execution flow.
To use Tracer
to broadcast the event, we need to inject our Tracer
to call the correct method
at the right place.
So how could we use the Tracer
to instrument the code? The answer to this is by using plugins support from Babel
.
First, we’ll transform the code
submitted from the frontend into Abstract Syntax Tree(AST), then we'll traverse the AST to look for the marker
to inject our code. To understand how this is possible, read here.
Using the custom babel plugin written, the code
will be transformed as shown below:
Tracer
does come with a feature that will limit the loop to the time limit set by TIMEOUT_MILLIS
.
Async-Hooks
By using async-hooks
, we're able to spy on the stage of an asynchronous
operation, which is the whole point of this visualizer. There is a total of five hook
that is supported by the API, as follow:
init
before
after
destroy
promiseResolve
Example of the hook
registered for promiseResolve
as follows:
We broadcast events as it occurs by calling postEvent()
For more information on how async-hooks
work, read here.
Conclusion
We’ve successfully built a visualizer for Node.js Event Loop, the live demo available here. Frontend & Backend code is pushed to Github if you would like to learn more about the implementation.
This project has been made possible by prior work from Andrew Dillon & Philip Roberts.