back

by bikeshaving·6y ago·view on hn ↗
Hi, author here. Glad to hear that you enjoyed the blog post.

> I would like to hear the author's thoughts on Concurrent Mode in general, and how Crank.js addresses the same issues. There's no mention here of time slicing, which is what Concurrent Mode really unlocks

I think Concurrent Mode is a really interesting project, but I also think that the developer experience of using CM as the React team has defined it is really lacking.

There’s the issue of querying when a specific component (or the whole tree) has finished rendering. With React, rendering is treated as a black box, and this was mostly okay when rendering was synchronous, but people often ask with concurrent mode, how do I know when rendering has finished, locally or globally. The answer the React maintainers would give would be, probably, useEffect hooks, but that’s not enough for me.

Promises are like the Planck constant of asynchrony in javascript; they’re the smallest units of time possible for executing code later and their callbacks have the highest priority. To not have a promise-backed API for CM seems like a step backwards. Even if I were to implement time slicing, I would want it to be promise based. Also, React fiber time slicing comes at a cost, in the sense that you have to check the clock for every unit of work to make sure you don’t exceed your budget. I would want any sort of priority-based it to be done on an opt-in, limited basis. I definitely don’t think every component should be a possible asynchronous breakpoint, that sounds like a nightmare to debug.

I dunno, I need to write a longer, better structured blog post about this stuff, but here’s something to think about: if you want concurrent mode or time slicing or scheduling priorities, the tools to implement it in user space are probably already there with Crank.

1 comments
I haven't used concurrent mode in anger yet, but I have used frameworks built on top of React that introduce their own async scheduler for rendering - Reagent, re-frame in ClojureScript-land.

You end up with two issues IME with async tasks (rendering being one of them):

- Making everything async - without prioritizations - means that at some point you have simply moved the problem into the scheduler. Just like taking a sequence of sync operations and wrapping them in a promise chain doesn't speed them up, likewise making every render async by itself doesn't speed things up at all (on the contrary it can make things slower).

- Using prioritized async tasks only allows you to prioritize the things which participate. If a component renders, or any other computation occurs, and isn't sent to the scheduler, it can steal work from higher priority things because the scheduler doesn't control it.

This is why IME a la carte async async rendering isn't super useful. It is a lot of mental overhead (and potentially laborious) to manage your application's schedule while also trying to build features. It would be interesting to have tools to tune it when necessary (which I assume React will provide in the form of ways to schedule computations at different priorities), but having it opt-in means that the default case is everything will steal work from the things that you are actually marking as high priority!

One thing I really want to make sure with Crank is that sync stays sync and async stays async. One of my early formative programming experiences was having to track down a race condition in an om app I was working on which was caused by the renderer making my sync code asynchronous.

https://github.com/omcljs/om/issues/173