Awesome
Reactive polyglot
R.
(Ramda) stands for standard functional toolkit.
Comparison notes
Operator counterparts aren't and can't be fully equivalent.<br/> "Kinda the same" – is the current definition.
The following projects, were created with different goals and tradeoffs in mind, so every comparison and analogy is subjective and can be argued. The presence of some operator is not necessary good, as well as the abscence is not necessary bad.
Note, that primitives differ for each library. In descriptions, we broadly refer to all the "observable primitives" as streams*, though, technically speaking, some of them are rather stream-like entities.
To find something, search for a term you know.
API
Create
Create an empty stream
Create a stream from a single value
Create a stream from an array
Create a stream from a promise
- KefirJS:
fromPromise
- MostJS:
fromPromise
- RxJS:
fromPromise
- XStream:
fromPromise
Create a stream from an event
- KefirJS:
fromEvents
- MostJS:
fromEvent
- RxJS:
fromEvent
- XStream:
fromEvent
Create a stream from a callback
- KefirJS:
stream
,fromCallback
,fromNodeCallback
,fromPoll
- MostJS:
new Stream
- RxJS:
new Observable
,bindCallback
,bindNodeCallback
- XStream:
? (make a promise first)
Prepend a stream with a value
Transform (data events)
Map value one-to-one
Filter value by a predicate
Skip N initial values
Take N initial values
Make a value from an accumulator and a next value
Combine
Merge multiple streams together
Pay attention that Kefir's merge
accepts arrays while others are variadic.
Combine multiple streams together
- KefirJS:
combine
- MostJS:
combine
- RxJS:
combineLatest
- XStream:
combine
Sample a stream by another stream
- KefirJS:
combine
,sampledBy
- MostJS:
sample
,sampleWith
- RxJS:
sample
,withLatestFrom
- XStream:
sampleCombine
Chain streams sequentially
- KefirJS:
flatMapConcat
- MostJS:
concatMap
- RxJS:
concatMap
- XStream:
map + flattenSequentially
Create
Create stream from non-stream values.
<table> <tr> <th>KefirJS</th> <th>MostJS</th> <th>RxJS</th> <th>XStream</th> </tr> <tr> <td><code>interval</code></td> <td><code>periodic</code></td> <td><code>interval + map</code></td> <td><code>periodic</code></td> </tr> <tr> <td><code>repeat</code></td> <td><code>of + R.range</code></td> <td><code>repeat</code></td> <td><code>of + R.range</code></td> </tr> <tr> <td><code>?</code></td> <td><code>iterate</code></td> <td><code>generate</code></td> <td>?</td> </tr> <tr> <td><code>?</code></td> <td><code>generate</code></td> <td><code>generate</code></td> <td>?</td> </tr> </table>Mapper
Modify events one to one.
<table> <tr> <th>KefirJS</th> <th>MostJS</th> <th>RxJS</th> <th>XStream</th> </tr> <tr> <td><code>delay</code></td> <td><code>delay</code></td> <td><code>delay</code></td> <td><code>combine(delay(500))</code></td> </tr> <tr> <td><code>– (map)</code></td> <td><code>timestamp</code></td> <td><code>timestamp</code></td> <td><code>– (map)</code></td> </tr> </table>Transforms
Modify events * to *.
<table> <tr> <th>MostJS</th> <th>RxJS</th> <th>XStream</th> </tr> <tr> <td><code>chain / flatMap</code></td> <td><code>flatMap</code></td> <td><code>map + flattenConcurrently</code></td> </tr> <tr> <td><code>map + switch</code></td> <td><code>switchMap / flatMapLatest</code></td> <td><code>map + flatten</code></td> </tr> <tr> <td><code>join</code></td> <td><code>mergeAll</code></td> <td><code>flatten</code></td> </tr> <tr> <td><code>loop</code></td> <td><code>scan + map</code></td> <td><code>fold + map</code></td> </tr> <tr> <td><code>– (<a href="bufferWithCount.md">custom</a>)</code></td> <td><code>bufferWithCount</code></td> <td>?</td> </tr> </table>Filters
Skip events by predicate or signal.
<table> <tr> <th>MostJS</th> <th>RxJS</th> <th>XStream</th> </tr> <tr> <td><code>skipRepeats</code></td> <td><code>distinctUntilChanged</code></td> <td><code>dropRepeats</code></td> </tr> <tr> <td><code>skipRepeatsWith</code></td> <td><code>– (scan)</code></td> <td><code>dropRepeats</code></td> </tr> <tr> <td><code>slice</code></td> <td><code>skip + take</code></td> <td><code>drop + take</code></td> </tr> <tr> <td><code>skipWhile</code></td> <td><code>skipWhile</code></td> <td><code>fold + filter + map</code></td> </tr> <tr> <td><code>takeWhile</code></td> <td><code>takeWhile</code></td> <td><code>filter + endWhen</code></td> </tr> <tr> <td><code>since / skipUntil</code></td> <td><code>skipUntil</code></td> <td><code>fold + filter + map</code></td> </tr> <tr> <td><code>until / takeUntil</code></td> <td><code>takeUntil</code></td> <td><code>filter + endWhen</code></td> </tr> <tr> <td><code>during</code></td> <td><code>window + take(1)</code></td> <td>?</td> </tr> </table>Combinators
Combine multiple streams into single.
<table> <tr> <th>MostJS</th> <th>RxJS</th> <th>XStream</th> </tr> <tr> <td><code>zip</code></td> <td><code>zip</code></td> <td>?</td> </tr> <tr> <td><code>concat</code></td> <td><code>concat</code></td> <td><code>concat</code></td> </tr> <tr> <td><code>ap</code></td> <td><code>combineLatest</code></td> <td>?</td> </tr> </table>Side effects
Produce side effect for every event.
<table> <tr> <th>MostJS</th> <th>RxJS</th> <th>XStream</th> </tr> <tr> <td><code>tap</code></td> <td><code>do / tap</code></td> <td><code>debug</code></td> </tr> </table>Ending
Operators which target end event somehow.
<table> <tr> <th>MostJS</th> <th>RxJS</th> <th>XStream</th> </tr> <tr> <td><code>empty</code></td> <td><code>empty</code></td> <td><code>empty</code></td> </tr> <tr> <td><code>never</code></td> <td><code>never</code></td> <td><code>never</code></td> </tr> <tr> <td><code>continueWith</code></td> <td><code>?</code></td> <td><code>concat</code></td> </tr> </table>Concurrency
<table> <tr> <th>MostJS</th> <th>RxJS</th> </tr> <tr> <td><code>– (<a href="https://github.com/ivan-kleshnin/reactive-polyglot/wiki/race">custom</a>)</code></td> <td><code>amb / race</code></td> </tr> </table>History
<table> <tr> <th>MostJS</th> <th>RxJS</th> </tr> <tr> <td colspan="2"><code>– (<a href="history.md">custom</a>)</code></td> </tr> </table>Design diffs
RxJS
- Three primitives:
Observer
,Observable
,Subject
. - Observables end on error.
- Provides API to handle errors.
- Does not provide API to handle ending.
KefirJS
- Two primitives:
Stream
andProperty
(like XStream). - Observables does not end on error (by default).
- Provides API to handle errors.
- Provides API to handle ending.
MostJS
- One primitive:
Stream
(+ community-driven). - Separate packages for subject-like and property-like primitives.
- Provides API to handle errors.
- Provides API to handle ending.
XStream
- Two primitives:
Stream
andMemoryStream
(like KefirJS). - Always multicast. A Stream is like an RxJS Subject.
- Streams end on error.
Common
RxJS does not emit initial scan
value as event (use startWith
for that).
Rx.Observable.interval(100).map(x => 1)
.scan(add, 0);
.subscribe(console.log); // 1--2--3--...
Most.periodic(100, 1)
.scan(add, 0);
.observe(console.log); // 0--1--2--3--...
Found docs / API quirks
MostJS
startWith
vs sampleWith
vs continueWith
+ recoverWith
vs skipRepeatsWith
<br/>
(val vs none vs func vs stream)
tap
is listed in "Transform" section.
RxJS
startWith
is listed in "Combine" section.
mergeAll
is listed in "Combine" section.
distinct
is not listed in "Filtering" section.
takeUntil
is not listed in "Filtering" section.
just
/ return
should be deprecated in favor of of
.
fromArray
should be deprecated in favor of from
.
Links
bacon-vs-kefir – BaconJS vs KefirJS API comparison
dataflows – web arch. dataflow comparison
stream-conversions – tool for cross-library stream conversions
Additional Read
https://github.com/cujojs/most/issues/171
https://twitter.com/rpominov/status/689566111734599683
https://github.com/zenparsing/es-observable/issues/66
Reactive polyglot
R.
(Ramda) stands for standard functional toolkit.
Comparison notes
Operator counterparts aren't and can't be fully equivalent.<br/> "Kinda the same" – is the current definition.
The following projects, were created with different goals and tradeoffs in mind, so every comparison and analogy is subjective and can be argued. The presence of some operator is not necessary good, as well as the abscence is not necessary bad.
Note, that primitives differ for each library. In descriptions, we broadly refer to all the "observable primitives" as streams*, though, technically speaking, some of them are rather stream-like entities.
To find something, search for a term you know.
API
Create
Create an empty stream
Create a stream from a single value
Create a stream from an array
Create a stream from a promise
- KefirJS:
fromPromise
- MostJS:
fromPromise
- RxJS:
fromPromise
- XStream:
fromPromise
Create a stream from an event
- KefirJS:
fromEvents
- MostJS:
fromEvent
- RxJS:
fromEvent
- XStream:
fromEvent
Create a stream from a callback
- KefirJS:
stream
,fromCallback
,fromNodeCallback
,fromPoll
- MostJS:
new Stream
- RxJS:
new Observable
,bindCallback
,bindNodeCallback
- XStream:
? (make a promise first)
Prepend a stream with a value
Transform (data events)
Map value one-to-one
- KefirJS:
map
- MostJS:
map
,constant
- RxJS:
map
,mapTo
- XStream:
map
Filter value by a predicate
- KefirJS:
filter
- MostJS:
filter
- RxJS:
filter
- XStream:
filter
Skip N initial values
- KefirJS:
skip
- MostJS:
skip
- RxJS:
skip
- XStream:
drop
Take N initial values
- KefirJS:
take
- MostJS:
take
- RxJS:
take
- XStream:
take
Make a value from an accumulator and a next value
- KefirJS:
scan
- MostJS:
scan
- RxJS:
scan
- XStream:
fold
Combine
Merge multiple streams together
- KefirJS:
merge
- MostJS:
merge
- RxJS:
merge
- XStream:
merge
Pay attention that Kefir's merge
accepts arrays while others are variadic.
Combine multiple streams together
- KefirJS:
combine
- MostJS:
combine
- RxJS:
combineLatest
- XStream:
combine
Sample a stream by another stream
- KefirJS:
combine
,sampledBy
- MostJS:
sample
,sampleWith
- RxJS:
sample
,withLatestFrom
- XStream:
sampleCombine
Chain streams sequentially
- KefirJS:
flatMapConcat
- MostJS:
concatMap
- RxJS:
concatMap
- XStream:
map + flattenSequentially
Create
Create stream from non-stream values.
<table> <tr> <th>KefirJS</th> <th>MostJS</th> <th>RxJS</th> <th>XStream</th> </tr> <tr> <td><code>interval</code></td> <td><code>periodic</code></td> <td><code>interval + map</code></td> <td><code>periodic</code></td> </tr> <tr> <td><code>repeat</code></td> <td><code>of + R.range</code></td> <td><code>repeat</code></td> <td><code>of + R.range</code></td> </tr> <tr> <td><code>?</code></td> <td><code>iterate</code></td> <td><code>generate</code></td> <td>?</td> </tr> <tr> <td><code>?</code></td> <td><code>generate</code></td> <td><code>generate</code></td> <td>?</td> </tr> </table>Mapper
Modify events one to one.
<table> <tr> <th>KefirJS</th> <th>MostJS</th> <th>RxJS</th> <th>XStream</th> </tr> <tr> <td><code>delay</code></td> <td><code>delay</code></td> <td><code>delay</code></td> <td><code>combine(delay(500))</code></td> </tr> <tr> <td><code>– (map)</code></td> <td><code>timestamp</code></td> <td><code>timestamp</code></td> <td><code>– (map)</code></td> </tr> </table>Transforms
Modify events * to *.
<table> <tr> <th>MostJS</th> <th>RxJS</th> <th>XStream</th> </tr> <tr> <td><code>chain / flatMap</code></td> <td><code>flatMap</code></td> <td><code>map + flattenConcurrently</code></td> </tr> <tr> <td><code>map + switch</code></td> <td><code>switchMap / flatMapLatest</code></td> <td><code>map + flatten</code></td> </tr> <tr> <td><code>join</code></td> <td><code>mergeAll</code></td> <td><code>flatten</code></td> </tr> <tr> <td><code>loop</code></td> <td><code>scan + map</code></td> <td><code>fold + map</code></td> </tr> <tr> <td><code>– (<a href="bufferWithCount.md">custom</a>)</code></td> <td><code>bufferWithCount</code></td> <td>?</td> </tr> </table>Filters
Skip events by predicate or signal.
<table> <tr> <th>MostJS</th> <th>RxJS</th> <th>XStream</th> </tr> <tr> <td><code>skipRepeats</code></td> <td><code>distinctUntilChanged</code></td> <td><code>dropRepeats</code></td> </tr> <tr> <td><code>skipRepeatsWith</code></td> <td><code>– (scan)</code></td> <td><code>dropRepeats</code></td> </tr> <tr> <td><code>slice</code></td> <td><code>skip + take</code></td> <td><code>drop + take</code></td> </tr> <tr> <td><code>skipWhile</code></td> <td><code>skipWhile</code></td> <td><code>fold + filter + map</code></td> </tr> <tr> <td><code>takeWhile</code></td> <td><code>takeWhile</code></td> <td><code>filter + endWhen</code></td> </tr> <tr> <td><code>since / skipUntil</code></td> <td><code>skipUntil</code></td> <td><code>fold + filter + map</code></td> </tr> <tr> <td><code>until / takeUntil</code></td> <td><code>takeUntil</code></td> <td><code>filter + endWhen</code></td> </tr> <tr> <td><code>during</code></td> <td><code>window + take(1)</code></td> <td>?</td> </tr> </table>Combinators
Combine multiple streams into single.
<table> <tr> <th>MostJS</th> <th>RxJS</th> <th>XStream</th> </tr> <tr> <td><code>zip</code></td> <td><code>zip</code></td> <td>?</td> </tr> <tr> <td><code>concat</code></td> <td><code>concat</code></td> <td><code>concat</code></td> </tr> <tr> <td><code>ap</code></td> <td><code>combineLatest</code></td> <td>?</td> </tr> </table>Side effects
Produce side effect for every event.
<table> <tr> <th>MostJS</th> <th>RxJS</th> <th>XStream</th> </tr> <tr> <td><code>tap</code></td> <td><code>do / tap</code></td> <td><code>debug</code></td> </tr> </table>Ending
Operators which target end event somehow.
<table> <tr> <th>MostJS</th> <th>RxJS</th> <th>XStream</th> </tr> <tr> <td><code>empty</code></td> <td><code>empty</code></td> <td><code>empty</code></td> </tr> <tr> <td><code>never</code></td> <td><code>never</code></td> <td><code>never</code></td> </tr> <tr> <td><code>continueWith</code></td> <td><code>?</code></td> <td><code>concat</code></td> </tr> </table>Concurrency
<table> <tr> <th>MostJS</th> <th>RxJS</th> </tr> <tr> <td><code>– (<a href="https://github.com/ivan-kleshnin/reactive-polyglot/wiki/race">custom</a>)</code></td> <td><code>amb / race</code></td> </tr> </table>History
<table> <tr> <th>MostJS</th> <th>RxJS</th> </tr> <tr> <td colspan="2"><code>– (<a href="history.md">custom</a>)</code></td> </tr> </table>Design diffs
RxJS
- Three primitives:
Observer
,Observable
,Subject
. - Observables end on error.
- Provides API to handle errors.
- Does not provide API to handle ending.
KefirJS
- Two primitives:
Stream
andProperty
(like XStream). - Observables does not end on error (by default).
- Provides API to handle errors.
- Provides API to handle ending.
MostJS
- One primitive:
Stream
(+ community-driven). - Separate packages for subject-like and property-like primitives.
- Provides API to handle errors.
- Provides API to handle ending.
XStream
- Two primitives:
Stream
andMemoryStream
(like KefirJS). - Always multicast. A Stream is like an RxJS Subject.
- Streams end on error.
Common
RxJS does not emit initial scan
value as event (use startWith
for that).
Rx.Observable.interval(100).map(x => 1)
.scan(add, 0);
.subscribe(console.log); // 1--2--3--...
Most.periodic(100, 1)
.scan(add, 0);
.observe(console.log); // 0--1--2--3--...
Found docs / API quirks
MostJS
startWith
vs sampleWith
vs continueWith
+ recoverWith
vs skipRepeatsWith
<br/>
(val vs none vs func vs stream)
tap
is listed in "Transform" section.
RxJS
startWith
is listed in "Combine" section.
mergeAll
is listed in "Combine" section.
distinct
is not listed in "Filtering" section.
takeUntil
is not listed in "Filtering" section.
just
/ return
should be deprecated in favor of of
.
fromArray
should be deprecated in favor of from
.
Links
bacon-vs-kefir – BaconJS vs KefirJS API comparison
dataflows – web arch. dataflow comparison
stream-conversions – tool for cross-library stream conversions
Additional Read
https://github.com/cujojs/most/issues/171