Building a Node.js Event Loop Visualizer

Phakorn Kiong
AIA Singapore Technology Blog
3 min readJun 12, 2021

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.

  1. 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 Tracerusing 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:

  1. init
  2. before
  3. after
  4. destroy
  5. 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.

Useful Reference:

Instrumenting Javascript by Tom Lieber

Node.js Event Loop by Deepal Jayasekara

--

--