The Event Loop That Wasn’t: What Happens to a Reactive System When You Remove JavaScript
Abstract
Fine-grained reactive systems built for the browser rely on implicit host facilities and guarantees, including promises, an event loop, a microtask checkpoint, and a garbage collector. We report on porting the reactive core of SolidJS to Rust, with those assumptions removed, and on what the removal revealed about which of them the system actually needs. Our headline finding is that SolidJS's async layer requires no asynchronous runtime. Upstream never polls a future; it registers a continuation and lets the platform make the call. Since the synchronous port already contained the queue that makes calls, the async layer landed with no executor, no futures, and no threads. A fine-grained reactive system needs the platform to queue continuations, drain them at a checkpoint, and let producers signal when asynchronous work settles. Compiling the port to WebAssembly tests the claim in its strongest form: the core, including asynchronous computations, runs as a 113.5 KiB module with no host imports. A binding to a browser engine whose DOM is a native node arena rather than a JavaScript API then measures the boundary using counts asserted for the native path and derived from emitted signatures for generated glue: one crossing per DOM operation versus two, while copying the same number of bytes as the glue toolchain's best configuration. The evaluation also reports where the arrangement loses, specifically in sixteen of twenty-five timed configurations under an interpreting runtime. It presents the porting method, the representation changes required by Rust, and four implementation questions plus one discrepancy between the specification and implementation uncovered during the port. The port reproduced the required scheduling without an event loop but could not reproduce the garbage collector's reachability guarantees.