Yes, this is serious, even if you have over 10 years experience using Observables, you got used to call fromEvent()
or .subscribe()
/.unsubscribe()
manually.
const fib = new Subject().pipe(
scan(([a, b], _, i) => [b, a +b +(i==1)], [0, 0]),
map(([_, b]) => b),
);
fib.subscribe(console.log);
fib.next();
fib.next();
fib.next();
// Using RxJS
fromEvent(document, 'click').subscribe(fib);
Sure, that's what everyone has been telling you, but what if I said this is not just inconvenient and repetitive (which you may have figured already), but not even necessary anymore?
The idea, in line with the concept of Extensible Effects but applied to reactive streams, lies in organising code in two parts: your streams first, followed by a declaration of their bindings.
When using JavaScript Components, stream bindings can be declared with HTML templates:
const template = rml`
<div>${fib}</div>
`;
Each stream should be pure: same data out given the same input.
Bindings declare how to feed your streams from the real world and
where does their output go.
Mutability and the Supposed Subject Anti-Pattern
In functional programming mutable data is the Antichrist Antipattern.
In RxJS you're able to push data into a Subject
and BehaviorSubject
imperatively, which can cause the same sorts of issues as mutable data.
Stream Oriented programming doesn't mandate many things. Feeding streams some data is typically left for framework to deal with. You're still not supposed to feed streams imperatively, so if you're ok with that, the bare use of Subject or BehaviorSubject is no longer an anti-pattern per se, but the very essence of the Stream Oriented paradigm.
The pure, Stream-Oriented Fibonacci Sequence
So, if the Fibonacci Sequence is now the output of a stream, and all we want to do is define the stream and a binding that says when we want the next value and where to put it, we'll have something like the following:
import { Subject, map, scan } from 'rxjs';
import { AppendHTML, rml } from 'rimmel';
const App = () => {
const fib = new Subject().pipe(
scan(([a, b], _, i) => [b, a +b +(i==1)], [0, 0]),
map(([_, b]) => b),
map(n=>`<li>${n}</li>`)
);
return rml`
<button onclick="${fib}">next</button>
<ul>${
AppendHTML(fib)
}</ul>
`;
};
document.body.innerHTML = App();
Simple enough? We have it ready to run on Stackblitz. Fork it and have fun.
Why didn't everyone just do that from start?
The Stream Oriented paradigm is a new-ish paradigm in web development. Streams have been around for quite some time (RxJS), templates, as well (JSX, Tagged Templates, etc). Not so many people thought about combining these together.
Some people did, but kept it just as an idea without taking it further (it takes quite some time and willpower to build a good JS framework).
The first JavaScript library designed around streams in this paradigm is Rimmel.js.
Take it for a spin and discover how the Stream-Oriented paradigm can make it easier than ever to create top quality webapps that just work.
Top comments (0)