On The Subject Of Subjects (in RxJS)

// What people usually first do with Subjects when they find them
// (TRY NOT TO DO THIS)
const subject = new Subject();
button.addEventListener(‘click’, () => subject.next('click');
subject.subscribe(x => console.log(x));
// This is better, but use Observable.fromEvent(button, 'click')
// instead, please.
const clicks = new Observable(observer => {
const handler = (e) => observer.next(e);
button.addEventListener('click', handler);
return () => button.removeEventListener('click', handler);
});

The Observer Pattern

(Source: Wikipedia)
const subject = new Subject();// add observer1 to the list of observers
const sub1 = subject.subscribe(observer1);
// add observer2 to the list of observers
const sub2 = subject.subscribe(observer2);
// notify all observers in the list with "hi there"
subject.next('hi there');
// remove observer1 from the list
sub1.unsubscribe();

Subject Compared To Observable

// To "share" the observable tick$ with two observers,
// observer1 and observer2, we can pipe all notifications
// through a Subject, like so
const tick$ = Observable.interval(1000);
const subject = new Subject();
subject.subscribe(observer1);
subject.subscribe(observer2);
tick$.subscribe(subject);

Subjects Are Not Reusable

// The death of a Subject
const subject = new Subject();
subject.subscribe(x => console.log(x));subject.next(1); // 1
subject.next(2); // 2
subject.complete();
subject.next(3); // silently ignored
subject.unsubscribe();
subject.next(4); // Unhandled ObjectUnsubscribedError

Gotchas in RxJS

// Demonstrating re-throwing for lack of an error handlerconst badObservable = Observable.throw(new Error('haha'));try {
badObservable.subscribe({
next: x => console.log(x),
error: null,
complete: () => console.log('done')
});
} catch (err) {
console.error(err); // logs our Error: "haha"
}
for (let observer of observers) {
observer.next('notify'); // What happens if this call throws??
}
// HINT: It's going to error and break the loop
// NOTE: Okay, that was more than a HINT.
// This is going to behave strangelyconst source$ = Observable.interval(1000).share();const mapped$ = source$.map(x => {
if (x === 1) {
throw new Error('oops');
}
return x;
});
source$.subscribe(x => console.log('A', x));
mapped$.subscribe(x => console.log('B', x));
source$.subscribe(x => console.log('C', x));
// "A" 0
// "B" 0
// "C" 0
// "A" 1
// Uncaught Error: "oops"
const source$ = Observable.interval(1000)
.share()
.observeOn(Rx.Scheduler.asap); // magic here
const mapped$ = source$.map(x => {
if (x === 1) {
throw new Error('oops');
}
return x;
});
source$.subscribe(x => console.log('A', x));
mapped$.subscribe(x => console.log('B', x));
source$.subscribe(x => console.log('C', x));
// "A" 0
// "B" 0
// "C" 0
// "A" 1
// Uncaught Error: "oops"
// "C" 1
// "A" 2
// "C" 2
// "A" 3
// "C" 3
// ... etc
const source$ = Observable.interval(1000)
.share()
.observeOn(Rx.Scheduler.asap); // magic here
const mapped$ = source$.map(x => {
if (x === 1) {
throw new Error('oops');
}
return x;
});
source$.subscribe(x => console.log('A', x));
mapped$.subscribe(
x => console.log('B', x),
err => console.log('Error handled: ' + err.message)
);
source$.subscribe(x => console.log('C', x));
// "A" 0
// "B" 0
// "C" 0
// "A" 1
// "Error handled: oops"
// "C" 1
// "A" 2
// "C" 2
// "A" 3
// "C" 3
// ... etc

The Future Of Observable

Summary

  1. Subjects are both observer and observable
  2. Subjects “multicast” to an internal list of observers
  3. Observables are just functions that set up observation
  4. Observables currently don’t trap errors but they should
  5. Errors thrown synchronously downstream from a Subject can kill the Subject
  6. You can use an error handler or observeOn to fix the problem in #4
  7. I was wrong about Promise error trapping. It’s a good idea, because promises are multicast.*
  8. Future versions of RxJS are likely to trap errors.
  • Although maybe not totally necessary, as promises are always async. (shrug)

--

--

--

RxJS Lead. Views are my own

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Ben Lesh

Ben Lesh

RxJS Lead. Views are my own

More from Medium

How to add Bootstrap to the Angular Project

Understanding Dragula.js (Drag-n-Drop library) events

Using Template-Driven Forms in Angular

Angular Best Practices — 2022

Angular