Suspense and Concurrent Mode are being touted as the future of web development by many. This set of features being developed by React is poised to revolutionize how web applications are designed. But these are complicated topics and while Dan Abramov’s series of tweets have helped to promote understanding of React’s intention with these features, I still feel the general public doesn’t get it yet. More so I do not think they have any means to compare this to other approaches. So I feel like I’ve come across some key insights when developing the same feature set for a Reactive library of all things. No Virtual DOM here folks. So while I explore the nuances of Solid’s solution I hope at the same time to provide some insights on what is really going on.

Asynchronous Rendering vs Fine-Grained Reactivity

Asynchronous Rendering, Time-Slicing, and finally Concurrent Mode have all been titles for this one feature at some point. The progression of naming is fairly insightful in showing the React team’s progression of the feature they were exploring. I’m going to say at the base this is a Virtual DOM problem that is being solved. This is not particularly impressive from a reactive library standpoint. However, Concurrent Mode is capable of so much more than the simple case.

In the most basic sense where this starts is allowing React to interrupt rendering and continue when sensible. See the problem with a Virtual DOM library like React is it is based on Top-Down Reconciliation. That means that whenever there is a change all downstream Components re-render. There are optimizations to prevent this (like shouldComponentUpdate ) but on initial render, this isn’t something you can optimize out as the work needs to happen. So in the middle of a render, blocking part of the rendering based on some state (like with an if statement) and triggering a scheduled timer can still end up invoking significant work.

However, the same with a reactive library like Solid is not the case. There is no reconciliation. It can just continue the work where it left off. One way of thinking of it might be that React 16 and the Fiber engine just gave the Virtual DOM similar granularity to a reactive library. But this isn’t anything new. KnockoutJS had this capability back in 2010.

Scheduling & Animation Benchmarks

If that was the end of the story I don’t think anyone would be talking about this. The first React Fiber demos showed React’s new ability to schedule work allowing for smooth animations that React 15 was nowhere near to matching. The most popular being the Sierpinski Triangle Demo. The benchmark highlights, in general, the misunderstanding about Time Slicing and demos of this nature. You see, after this, a bunch of libraries made their attempt and many of them put up better results than React 16 adding even more triangles etc… but this proved nothing.

There are 2 problems with all of these demos. First, this test aims to show graceful degradation during heavy load, not about maximizing performance. We are in a browser using the DOM. This isn’t some C program in your game engine. The cost of manipulating the DOM is like 100x more than anything you will do in the JavaScript when all things are optimized.

Scheduling is an exercise in doing less work. So patting ourselves on the back for it is premature. In raw terms, it is slower. We’ve seen this in benchmarks. Requesting an animation frame slows down rendering a large table to a screen. Ultimately scheduling is more overhead, but the benefits come from selectively keeping parts of the UI more responsive. But it means that benchmarking this stuff is pretty arbitrary. If 60fps is your ceiling you can write a lot of garbage in that 16ms frame and it does not matter much. However splitting the rendering, doing more DOM operations, is more expensive overall. The JS Framework Benchmark has been a harsh teacher on the cost of DOM operations. I’ve been taught this lesson many times on the road to being arguably the fastest JavaScript UI Framework out there.

Second, we are still constrained by the browser. Everyone has the same scheduling techniques at their disposal. setTimeout , requestAnimationFrame , Promise.resolve , etc… All React does in the demo is call requestIdleCallback . It may not be a common API but it is a native one. I made a Solid implementation wrapped a state propagation in the same handler and presto it worked just like React. Although if you look at this GitHub issue: https://github.com/claudiopro/react-fiber-vs-stack-demo/issues/3 I realized pretty quick this implementation was less than ideal. People with less powerful devices had noticed that React Fiber never updated the numbers in the circles. I was able to reproduce this easily with CPU throttling in Chrome. It’s not React’s fault it’s just how Chrome’s implementation of requestIdleCallback chose to handle the load. At that point, I realized that this was not that simple and the React implementation was. I set up a slightly more complicated scheduling mechanism with Solid that was arguably a touch less smooth but ensured updates. It was trivial because Solid already exposed the ability for this control.

Concurrent Mode

So it was pretty clear the basics just bring Virtual DOM performance for animation more inline with reactive libraries. The truth is a super-efficient Virtual DOM library like Inferno probably would do just fine without it. I’m pretty sure any amazing time-slicing demo someone comes up with will be easily matched by a reactive library like Solid or even Svelte. But that’s all besides the point. This is not about performance it’s about the experience and Concurrent Mode is a whole lot cooler than bad animation demos.

What makes concurrent mode amazing is not necessarily true concurrency, remembering JavaScript in the browser is mostly single-threaded. Experiments for offloading to worker threads while having shown promising results haven’t in this domain as they are unable to work on the DOM. Again the cost in DOM operations makes clever work delegation in most cases an overhead. Concurrent Mode allows maintaining multiple render states so that rendering can happen sooner while we’d otherwise be waiting, without the end consumer being any the wiser. The “Concurrent” in Concurrent Mode is that React can show you one thing while rendering the next thing offscreen. The potential here is huge. It starts with buffering as described here, which is what Suspense is all about but can also move into cool things like partial streaming hydration with SSR rendering or doing a predictive rendering of multiple possible futures (I hope someone coins this as Quantum Rendering). That is rare and interesting and something I knew I wanted to have in Solid.

Suspense

If you’ve been keeping track you realize that a fine-grained reactive library like Solid already has all the basics to support this built-in without an expensive rewrite. In so I’ve been able to keep up with React Experimental branch as it comes out since the model is mostly natural for Solid. However, there are some key differences. The biggest, of course, is that Solid is a reactive library. Developing the solution during this process felt a bit like FPGA programming if you’ve ever had the pleasure. Solid’s reactive system is based on Synchronous Reactive Programming and in so I felt a bit like I was programmatically designing the hardware that the end-user would be using. Development happened in 3 stages with each building off the previous.

1. Fallback Fundamentals