What's the difference between RxJS and IxJS, and when would I want to use one over the other?
From the IxJS docs:
RxJS is great for event-based workflows where the data can be pushed at the rate of the producer, however, IxJS is great at I/O operations where you as the consumer can pull the data when you are ready.
After going through the docs, the only major difference seems to be the concept of Iterables in IxJS and Observables in RxJS.
Both Iterables and Observables execute either synchronously or asynchronously, and the .forEach
from IxJS is essentially the same as RxJS's .subscribe
method when paired with the almost-identical from
creator function. The only other difference is IxJS's .forEach
method is optional because you could use the imperative for-of
instead.
It seems like there are two libraries for no reason because RxJS's from
creator function can convert Iterables to Observables.
From my perspective, it's not really IxJS and RxJS, it's Iterables and Observables. How are they different and when would you use one over the other?
RxJS processes values as soon as they arrive. It's a push system.
IxJS specifies when to pass in the next value. It's a pull system.
IxJS
may be helpful if want to have pull-based model, for example, when dealing with backpressure.
As you can see in the documentation:
IxJS unifies both synchronous and asynchronous pull-based collections, just as RxJS unified the world of push-based collections. RxJS is great for event-based workflows where the data can be pushed at the rate of the producer, however, IxJS is great at I/O operations where you as the consumer can pull the data when you are ready.
In other words:
RxJS
if your producer (usually User) is slower that processing of data (this is common for frontend).IxJS
if your producer (usually System) is much faster than you can process data (more common for the backend).To understand what this means, consider the following example:
You need to build ETL pipeline and process a large file (about 1TB).
If you write it with RxJS, something like:
readFileByLineObservable('path/to/file')
.pipe(
doSomeHeavyTransformation(),
)
.subscribe()
Then readFileByLineObservable
will try to "push" the entire file of 1TB into RAM as soon as possible. Only after this occurs, you will start to do doSomeHeavyTransformation
. This problem is called backpressure.
In contrast, IxJS
will try to "pull" each newline only after previous line was processed. That's the optimal processing method in this case.
The difference is how RxJS's .subscribe
sets up a listener whereas IxJS's .forEach
tells its iterator when to give the next value (only after its done processing the first one. It's similar to, but not the same as, RxJS's concatMap
and concatAll
operators.
As a complement to Oles Savluk answer, I found Matt Podwysocki's explanation particularly useful (https://gist.github.com/mattpodwysocki/1d0fe43961c6222571386568b8a5ef23):
we have four types of collections, each with their own purpose. Each has its own place, and there's no one solution that rules them all.
Pull: Iterable - purely synchronous data, either finite or infinite Push: Observable / Subject/Observer - eventual data such as DOM events, collections over time Pull/Push: AsyncIterable - I/O or other asynchronous data where the consumer needs to be in control Push/Pull: AsyncObservable - Network calls where creation/teardown may be asynchronous as well as projections may be asynchronous too.
Matt is a contributor to both RxJS and IxJS. Iterable and AsyncIterable come from IxJS, Observable and AsyncObservable are developed in RxJS
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With