DEV Community ๐Ÿ‘ฉโ€๐Ÿ’ป๐Ÿ‘จโ€๐Ÿ’ป

DEV Community ๐Ÿ‘ฉโ€๐Ÿ’ป๐Ÿ‘จโ€๐Ÿ’ป is a community of 967,611 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Cover image for Main aspects of reactivity
Jin
Jin

Posted on • Updated on

Main aspects of reactivity

Hello, my name is Dmitriy Karlovskiy, and I want to share with you the results of my 6-years research of reactivity to build a user interface.

It introduces a clear definition of reactivity, consisting of 6 bases. 4 fundamental requirements for the implementation of reactivity are introduced. Then 12 aspects of reactivity are analyzed in detail, which it is important when choosing a reactivity model. For each aspect, the behavior option that is most practical is selected. As a result, an idea is formed about how the most practical reactivity model should work. After that, well-known libraries and frameworks are taken, and compared according to the degree of approximation to the ideal.

This is a very large article written in Russian. I will keep it up to date. Therefore, I did not translate it manually and repost it, as it is very exhausting, and the result will quickly become outdated. So I suggest reading it either in the original or in an auto-translation:

However, if someone is willing to make and support an English version, I only welcome it. And, of course, be sure to let me know about any inaccuracies so that I correct them and do not mislead people.

Have fun reading, and may supersonic be with you!

Some spoilers..

Lib/FW Style Watch Dupes Origin Tonus Order Flow Error Cycle Depth Atomic Extern
$mol_wire ๐Ÿค“โœ… ๐Ÿคโœ… ๐ŸŽญโœ… ๐Ÿš‚โœ… ๐Ÿฆฅโœ… ๐Ÿ‘จโ€๐Ÿ’ปโœ… ๐Ÿš•โœ… ๐Ÿฆบโœ… ๐ŸŒ‹โœ… ๐Ÿ—ปโŒ ๐Ÿ‘ปโŒ ๐Ÿ‡โœ…
CellX ๐Ÿค“โœ… ๐Ÿคโœ… ๐Ÿ†”โŒ ๐Ÿš‚โœ… ๐Ÿฆฅโœ… ๐Ÿ‘จโ€๐Ÿ’ปโœ… ๐Ÿš•โœ… ๐Ÿฆบโœ… ๐ŸŒ‹โœ… ๐Ÿ—ปโŒ ๐Ÿฆถโœ… ๐Ÿ‡โœ…
MobX ๐Ÿค“โœ… ๐Ÿคโœ… ๐Ÿ†”โŒ ๐Ÿš‚โœ… ๐Ÿฆฅโœ… ๐Ÿ‘จโ€๐Ÿ’ปโœ… ๐Ÿš•โœ… ๐Ÿฆบโœ… ๐ŸŒ‹โœ… ๐Ÿ—ปโŒ ๐Ÿ‘ปโŒ ๐ŸŠโ€โ™‚๏ธโŒ
ChronoGraph ๐ŸงโŒ ๐Ÿคโœ… ๐Ÿ†”โŒ ๐Ÿš‚โœ… โฐโŒ ๐Ÿ‘จโ€๐Ÿ’ปโœ… ๐Ÿš•โœ… โฎโŒ ๐ŸŒ‹โœ… ๐ŸŒŒโœ… ๐Ÿคผโ€โ™‚๏ธโŒ ๐ŸŠโ€โ™‚๏ธโŒ
Reatom ๐ŸคฏโŒ ๐Ÿคโœ… ๐Ÿ†”โŒ ๐Ÿš‚โœ… ๐Ÿฆฅโœ… ๐ŸงจโŒ ๐ŸฆฝโŒ โฎโŒ ๐ŸšซโŒ ๐Ÿ—ปโŒ ๐Ÿคผโ€โ™‚๏ธโŒ ๐ŸŠโ€โ™‚๏ธโŒ
Effector ๐ŸคฏโŒ ๐Ÿคโœ… ๐Ÿ†”โŒ ๐ŸฅŒโŒ ๐Ÿ”โŒ ๐Ÿ“ฐโŒ ๐ŸฆฝโŒ ๐ŸŽฒโŒ ๐Ÿ’คโŒ ๐ŸŒŒโœ… ๐Ÿ‘ปโŒ ๐ŸŠโ€โ™‚๏ธโŒ
RxJS ๐ŸคฏโŒ ๐Ÿคโœ… ๐Ÿ‘ฏโ€โ™€๏ธโŒ ๐ŸฅŒโŒ ๐Ÿ”โŒ ๐Ÿ“ฐโŒ ๐ŸฆฝโŒ โ›”โŒ ๐ŸšซโŒ ๐Ÿ—ปโŒ ๐Ÿ‘ปโŒ ๐ŸŠโ€โ™‚๏ธโŒ
Vue ๐Ÿค“โœ… ๐Ÿคโœ… ๐Ÿ†”โŒ ๐Ÿš‚โœ… ๐Ÿฆฅโœ… ๐Ÿ‘จโ€๐Ÿ’ปโœ… ๐Ÿš•โœ… ๐Ÿฆบโœ… ๐ŸŽฐโŒ ๐Ÿ—ปโŒ ๐Ÿ‘ปโŒ ๐ŸŠโ€โ™‚๏ธโŒ
Ember ๐Ÿค“โœ… ๐Ÿ”ŽโŒ ๐Ÿ†”โŒ ๐Ÿš‚โœ… ๐Ÿฆฅโœ… ๐Ÿ‘จโ€๐Ÿ’ปโœ… ๐Ÿš•โœ… ๐ŸŽฒโŒ ๐ŸŒ‹โœ… ๐ŸšงโŒ ๐Ÿ‘ปโŒ ๐ŸŠโ€โ™‚๏ธโŒ
Solid ๐ŸงโŒ ๐Ÿคโœ… ๐Ÿ†”โŒ ๐Ÿš‚โœ… ๐Ÿ”โŒ ๐Ÿ‘จโ€๐Ÿ’ปโœ… ๐Ÿš•โœ… โ›”โŒ ๐ŸšซโŒ ๐Ÿ—ปโŒ ๐Ÿ‘ปโŒ ๐ŸŠโ€โ™‚๏ธโŒ
React ๐ŸงโŒ ๐Ÿ”ŽโŒ ๐Ÿ‘ฏโ€โŒ ๐ŸฅŒโŒ โฐโŒ ๐Ÿ‘จโ€๐Ÿ’ปโœ… ๐ŸฆฝโŒ โ›”โŒ ๐ŸŒ‹โœ… ๐ŸšงโŒ ๐Ÿ‘ปโŒ ๐Ÿ‡โœ…
Angular ๐ŸงโŒ ๐Ÿ”ŽโŒ ๐Ÿ†”โŒ ๐ŸฅŒโŒ โฐโŒ ๐Ÿ‘จโ€๐Ÿ’ปโœ… ๐Ÿš•โœ… ๐ŸŽฒโŒ ๐ŸŽฐโŒ ๐ŸšงโŒ ๐Ÿ‘ปโŒ ๐ŸŠโ€โ™‚๏ธโŒ
Svelte ๐ŸงโŒ ๐Ÿ”ŽโŒ ๐Ÿ†”โŒ ๐ŸฅŒโŒ โฐโŒ ๐Ÿ‘จโ€๐Ÿ’ปโœ… ๐Ÿš•โœ… โ›”โŒ ๐ŸšซโŒ ๐ŸŒŒโœ… ๐Ÿ‘ปโŒ ๐ŸŠโ€โ™‚๏ธโŒ

Top comments (15)

Collapse
 
3shain profile image
3Shain • Edited on

Very informative.
My project is not yet to be released but it almost meets all your "Practicality" level, except it uses Object.is by default (it's a performance concern, while you can set up custom comparator)

Collapse
 
ninjin profile image
Jin Author

Sounds interesting, what about the documentation?

Collapse
 
3shain profile image
3Shain

I'm currently working on it. The APIs just become stable recently. (Actually I've started writing documents for months, and lots of problems were reflected so it was refactored several times). We can talk about details on discord maybe?

Thread Thread
 
ninjin profile image
Jin Author

My voice english is too bad for discord.
You may be interested in the $mol_wire approach, where adding/removing a dependency takes O(1). In short: all cells store not only link to peer, but position of back link inside peer array too.

Thread Thread
 
3shain profile image
3Shain • Edited on

Typing text is OK ๐Ÿ˜‚ I speak poor English as well. I use the same algorithm before (so does s-js, solidjs). The reason why I choose linked list is, the dependencies list are often stable and ordered (and often only one dependency). So the optimization is to reuse the old list as many as possible. Ideally it will be just several read without write.
And I don't have to remove one specific dependency. If I found there is a mismatch, I will give up all remaining history records and build a new linked list from current cursor (and the remaining records will be cleanup later, in batch.)
So you can haveadd/remove O(1) , but what about no add/remove at all๐Ÿ˜‰
(also I don't get it why you emphasise O(1), is double linked list manipulation not O(1)?)

Thread Thread
 
3shain profile image
3Shain

Wait I think there should be some contexts. I'm taking about dependency tracking but I think you are talking about something else. Because I'm not using a pubsub model, but scheduling and topological sort

Thread Thread
 
Sloan, the sloth mascot
Comment deleted
 
ninjin profile image
Jin Author • Edited on

A linked list is very expensive in memory and slow because of the garbage collector. And iterating on it is significantly more expensive than a conventional array.

In my approach, if the dependencies do not change, then the cursor just runs through the array and that's it, no allocations. Dependency replacement also happens inplace. Adding/removing at the end is the usual push/pop, which does not lead to unnecessary memory allocation. The only point is that inserting/removing dependencies in the middle requires replacing dependencies to the end, but this is done again within the framework of O(1) on dependency tracking. And again, it does not require unnecessary realocations.


But public discussion in the comments is useful not only for the two of us, but also for other readers.)

Thread Thread
 
3shain profile image
3Shain • Edited on

very expensive
significantly more expensive

that is not true I got similar perf, even better.
You could argue in theory an array should be faster than linked list, but in javascript that really depends on a lot. If your array doesn't not store semi or double, the array is not that optimized, unlike in C you are just manipulating the memory. You could also argue that GC time isn't counted, but that's not a serious issue at all, we are using Javascriptโ„ข๏ธ, not to write a real time program. You should blame React who use closure like free resources. (btw, doesn't Array cause GC?)
I do use the method as you described before, and I had done a lot of experiments and benchmarks then I decided to turn to linked list. Using array doesn't gives me significant perf boost (like >10%). And it's not the only factor of performance, a lot things can affect (e.g. inline cache, algorithm: iterative than recursive). But most important thing is, a linked list model gives much less mental overhead. And I even have more to explore (e.g. FWIW concurrent mode like React (fiber is linked list implemented)).
To sum up, array implementation doesn't give me much in return. So I didn't use it.
You should still elaborate what's mean by O(1). I still simply think it's normal.

Thread Thread
 
ninjin profile image
Jin Author

It is also worth considering that this is a synthetic benchmark, where the nodes of a linked list are arranged sequentially. In reality, they will be scattered across memory, which is much slower.

I certainly don't use React. I have written a more efficient framework.)

Arrays allocate memory only when more elements need to be placed than have already been allocated for them. After "warming up", they usually stop allocating memory at all during the operation of the application.

I don't understand question about O(1).

Thread Thread
 
3shain profile image
3Shain • Edited on

I should have told you, a SMI_ELEMET (integer) array is optimized while normal array are not. But in your pub/sub.ts you definitely not just store a SMI (integer) but a hybrid of object and integer. That's why I use an object but not i directly. THIS CAN DROP PERFORMANCE A LOT.

And I carefully read your code, pretty much the same algorithm (about tracking dependency) with minor difference ( and of course different implementations). While I have to consider more like topological sorting/ on-demand subscription and a linked list model make it much more clear. I just want to say linked list is fast, even though array could be the fastest. Anyway you didn't sell me the point. I think your code is good and high-quality, but not impressive to me (my personally take, from my personal experience, I've done exactly the same thing).

Let's end the topic. It's much less funny than your work on the conclusions of reactivity.

Thread Thread
 
ninjin profile image
Jin Author • Edited on

Now Chrome stores both references to objects and integers up to a billion in 4 bytes. See the table in my article.. So arrays with it is actually PACKED_SMI_ELEMENT.

Thread Thread
 
3shain profile image
3Shain

latest node tested, it's PACKED_ELEMENTS.

Thread Thread
 
3shain profile image
3Shain • Edited on

Apart from this the test you posted is unfair. One is manipulating on array and another needs to access property which cause IC.And there are costs of object creation.
It's not C, how could you say a pointer is a number? Of course I know they are fixed-size.
( and if I remember correctly, 64-bit pointer should occupied 8 bytes, how is that a SMI? I find this but it doesn't mean a pointer could be a substitute of SMI. It's just about memory layout, but element kind is a strict assumption of data type, it has nothing to do with memory layout. Otherwise I could say everything is an object so everything could be stored like a SMI? Then what's the point to have element types.


Maybe I was wrong at something, as the object could be already created then pushed, unlike a linked node must be created when inserting. But I have did a lot of experiments between different implementations and haven't see a significant perf gap. I think maybe this is not the main contributor of performance overhead. (e.g. 1ms comparing to 10ms is huge different but 1001ms comparing to 1010ms is negligible). And there are several features which rely on linked list(e.g. the unmatched records is not replaced but kept to the end and clean in batch).......I think there are someone else who would be interested in your impl so I will share it to them.

Topic ends indeed. I will not reply anymore. If any further arguments, you are right :) .

Collapse
 
markerikson profile image
Mark Erikson

I just read through the auto-translated version of the post. Very impressive! You've clearly spent a lot of time thinking about this issue and coming up with categories. The diagrams were very helpful in understanding your ideas as well.

Would definitely love to see a fully-translated version of the post!

This post blew up on DEV in 2020:

js visualized

๐Ÿš€โš™๏ธ JavaScript Visualized: the JavaScript Engine

As JavaScript devs, we usually don't have to deal with compilers ourselves. However, it's definitely good to know the basics of the JavaScript engine and see how it handles our human-friendly JS code, and turns it into something machines understand! ๐Ÿฅณ

Happy coding!