6 ms·
Good suggestion, should probably address this in the readme. Short answer: observables do too much, and Rx is WAY, WAAAAY too large. We need simple async primi
by Mitranim 9y ago
Good suggestion, should probably address this in the readme.
Short answer: observables do too much, and Rx is WAY, WAAAAY too large. We need simple async primitives before layering bigger abstractions on them. They shouldn't take encyclopedic amount of reading to learn. They need to come in a library that doesn't weigh over 100 KB.
- antjanus 9y agoWhat about xstream? https://github.com/staltz/xstream https://github.com/staltz/xstream it's a lightweigth alternative with a fraction of Rx's operators and a fraction of its weight.
- Mitranim 9y agoSort of answered above: https://news.ycombinator.com/item?id=14963152 https://news.ycombinator.com/item?id=14963152
- Mitranim 9y agoI guess this needs a bigger answer. A well-designed tool should minimise the amount of states the program can be in. Asynchronous programming is already horrifically bad, it generates combinatorial explosions of intermediary states. Adding imperative, mutative programming and an event-driven API only exacerbates the problem, increasing the amount of possible state sequences even further. I don't think xtream provides a good API.
- shados 9y agoOkay. Most.js then, at 10kb minified looking at the build on unpkg. Also, implementing an observable from scratch is pretty much trivial. The operators are where all of the logic are, and they are arguably simpler to understand than alternatives because of how well documented they are. And if you limit yourself to the operators that are equivalent to what this library provides, there really wouldn't be much complexity to it.
- Mitranim 9y agoIn my experience, most async operations fit very well within the small feature set of promises/futures, with only a few operators (map/all/race). Streams should be the optional next layer of abstraction, not the only layer of abstraction. They should be built on top of futures. I quite like how Tokio did it for Rust futures. [1] Not a fan of Rx and similar designs. There are also different types of observables to consider. Rx, xtream, Most.js, they're basically stream libraries. When doing GUI programming, it's more useful to have reactive units with a _synchronous_ data access, because it matches how you want to access the data when drawing your view (e.g. in React). I ended using observables that look like Clojure's atoms (see Espo: [2]), with a special adapter for React (see Prax: [3]). Observable libraries like Rx were useless for that use case. [1] Example of decent future/stream design: https://tokio.rs https://tokio.rs [2] Observables focused on synchronous access: https://mitranim.com/espo/#-atom-value- https://mitranim.com/espo/#-atom-value- [3] React adapter for implicit reactivity, based on synchronous observables (impossible with async streams): https://mitranim.com/prax/ https://mitranim.com/prax/
- littlecranky67 9y agoAgreed, thats why there are proposals of introducing Observables as ES8 native datatypes. Note that while full RxJS is heavy, Observables are not. Its the multitude of operators to compose observables, and the various helper implementations of Subjects (AsyncSubject, ReplaySubject, BehaviorSubject etc.) - which are similar to Defereds in Promise world - that make RxJS so heavy. If you use only the Observable part with a slim selection of operators in your code, you will not add so much of the library to your code (but granted the patch-based API of RxJS is not really trivial).
- Mitranim 9y agoI believe we need a hierarchy of primitives: Level 0: one-value operations (promises = futures). Level 1: multi-value operations (streams = observables), implemented in terms of one-value operations. Tokio did it well for Rust (https://tokio.rs https://tokio.rs). Starting at level 1 doesn't feel right to me.
- littlecranky67 9y agoI'd rather have a clear separation of data structures and implemenations: Promise, Future, Observable = data structures Bluebird, Fluture, RxJS Observable = implementation of the data structures Operators (map/race/all) = helper implementations for operating the data structure implementations Now, different implementations may make different choices, like there are dozens of Promise implementations tuned for either feature richness, speed, or small file size. Same holds true for observables, there are feature-rich libs like RxJS, but also fast and/or small implementations (Bacon, most.js, xstream). My point: A cancelable promise is basically an observable that emits a single item (and caches that item). So an observable is kind of like a superset of a cancelable promise (and of course a non-cancelable promise). All operator implementations manipulating observables can directly be reused for cancelable and non cancelable promises-like observables; the other way around does NOT hold true. My main point: Just use observables, they can do everything that promises and cancelable promises can do and, if needed, a lot more. So instead of layering, learn to use this one data structure and you will be able to cope with a lot of async troubles.
- mayank 9y agoIs there a measurable performance impact caused by the 100 kb library size?
- Mitranim 9y agoYes. Depends on client bandwidth and CPU, but it's significant. In fact, it's insanely high. Not caring about "just another 100 KB" is how people end up with 2-5 MB bundles that take seconds to download on weak networks and seconds to execute on weak devices.
- littlecranky67 9y agoYou would have to epxlain how adding a 100kb library make your bundle become 2-5MB huge. And note that with RxJS 5 you can only pick operators and data structures that you need; I have written a Redux-clone using RxJS [1] which uses Observable, Subject and a handful of operators and is less than 16kb as minified & gzipped in total as self-contained umd bundle (of which half the size is attributed to lodash helper functions). [1]: https://github.com/Dynalon/reactive-state https://github.com/Dynalon/reactive-state
- Mitranim 9y agoThis is news to me. Last time I tried RxJS, was unable to get a usable core less than 100 KB minified (I don't use gzip as a metric). Would consider 20-30 KB. Might want to look again. Bundle size: a typical SPA imports multiple libraries, they import more libraries, and so on. It's not just Rx. I shouldn't have to explain how small things add up. Not caring about size is how it balloons up. We have to care about it in every library.
- dakom 9y agoSodiumFRP provides all the required primitives and is probably super tiny if combined and minified