DEV Community

Cover image for You've been using Observables all wrong!
Dario Mannu
Dario Mannu

Posted on • Edited on

You've been using Observables all wrong!

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);
Enter fullscreen mode Exit fullscreen mode

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>
`;
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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.


Learn More

Top comments (0)