RxJS Essential
Andrew Zhang
2021.8.12
Category
- Observable
- Cold vs Hot
- Subject
- Operators
- Memory Leak
Observable
Observable
- of
- range
- interval
- fromEvent
- fromPromise
Publisher
Observer
notify
subscribe
Obserable = Publisher + Iterator
import { Observable } from 'rxjs';
const source$ = new Observable(observer => {
  observer.next(1);
  observer.next(2);
  observer.next(3);
});
source$.subscribe(item => {
  console.log(item);
});
State
Normal
Error
Complete
next
complete
error
1
2
1
2
1
2
1
2
3
import { Observable } from 'rxjs';
new Observable(observer => {
  let number = 1;
  setInterval(() => {
    observer.next(number++);
  }, 1000);
});
new Observable(observer => {
  let number = 1;
  setInterval(() => {
    if (number > 2) {
      observer.complete();
    }
    observer.next(number++);
  }, 1000);
});
new Observable(observer => {
  let number = 1;
  setInterval(() => {
    if (number > 2) {
      observer.error();
    }
    observer.next(number++);
  }, 1000);
});
Cold vs Hot
Cold Observable



Observer
Observer
Observer
Observable
Observable
Observable
Unicast
1
2
3
1
2
3
Observer A
Observer B
interval, range, of
Hot Observable



Observer
Observer
Observer
Observable
Multicast
1
2
3
2
3
Observer A
Observer B
fromPromise, fromEvent
Cold 2 Hot
import { Observable } from "rxjs";
const cold = new Observable(observer => {
  observer.next(Math.random());
});
cold.subscribe(a => console.log(a));
cold.subscribe(b => console.log(b));
const producer = Math.random();
const hot = new Observable(observer => {
  observer.next(producer);
})
hot.subscribe(a => console.log(a))
hot.subscribe(b => console.log(b))
Hot
Cold
Lazy
Lazier
Subject
Subject
import { Subject, interval } from 'rxjs';
const tick$ = interval(1000)
const subject = new Subject();
tick$.subscribe(subject);
const subscription1 = subject.subscribe(
  item => console.log(item),
);
setTimeout(() => {
  const subscription2 = subject.subscribe(
    item => console.log(item),
  )
}, 1500);import { interval } from 'rxjs';
const tick$ = interval(1000);
const subscription1 = tick$.subscribe(
  item => console.log(item),
);
setTimeout(() => {
  const subscription2 = tick$.subscribe(
    item => console.log(item),
  );
}, 1500);
Subject = Observable + Observer
0
1
2
0
1
2
0
1
2
1
2
Subject
Cold
Observer
Observer
Observer
Cold
hot
BehaviorSubject
import { BehaviorSubject } from 'rxjs';
const subject$ = new BehaviorSubject(0);
const subscription1 = subject$.subscribe(
  item => console.log(item),
);
subject$.next(1);
subject$.next(2);
const subscription2 = subject.subscribe(
  item => console.log(item),
);
subject$.next(3);0, 1, 2, 3
2, 3
* 4
* 2
BehaviorSubject = Subject + shareReplay(1)
cache

Operators
Basic Operators
import { interval } from 'rxjs';
import { 
  map, 
  filter, 
  take, 
  scan, 
} from 'rxjs/operators';
interval(1000).pipe(
  map(n => Math.pow(n, 2)),
  scan((acc, val) => acc + val),
  filter(v => v > 10),
  take(3),
)import { interval } from 'rxjs';
import { 
  map, 
  filter, 
  take, 
  scan, 
} from 'rxjs/operators';
interval(1000).pipe(
  take(3),
  map(n => Math.pow(n, 2)),
  scan((acc, val) => acc + val),
  filter(v => v > 10),
);
map
scan
fitler
take
Combination
source1$
source2$
source1$.pipe(concat(source2$))
merge(source1$, source2$)
source1$.pipe(zip(source2$))
source1$.pipe(withLatestFrom(source2$))
combineLatest(source1$, source2$)
forkJoin(source1$, source2$)
import { merge, combineLatest, forkJoin } from 'rxjs';
import { concat, zip, withLatestFrom } from 'rxjs/operators';High-order Mapping
const source$ = interval(800);
source$.pipe(
  take(3),
  map(i => 
    interval(500).pipe(take(3))
  ),
)
High-order Observable
concatMap
mergeMap
switchMap
exhaustMap
const source$ = interval(800);
source$.pipe(
  take(3),
  concatMap(i => project(i)),
)
Back Pressure
interval(200).pipe(
  throttleTime(1000),
  debounceTime(1000),
)interval(200).pipe(
  throttleTime(1000),
)source
throttleTime
debounceTime
Error Handle
function throwOnUnluckyNumber(num) {
  if (num === 4) {
    throw new Error('unlucky number 4');
  }
  return num;
};
const source$ = interval(500).pipe(
  map(i => throwOnUnluckyNumber(i))
);
const subscription1 = source$.pipe(
  retry(1),
  catchError(err => of(8)),
  finalize(() => console.log('error or complete'))
)
// make it hot
const subject = new Subject();
source$.subscribe(subject);
const subscription2 = subject.pipe(
  retry(1),
  catchError(err => of(8)),
  finalize(() => console.log('error or complete'))
)Retry = Unsubscribe + Subscribe
Memory Leak
Unsubscribe
Timeline
No data stack
const source$ = new Subject();
 
let s = source$.subscribe(v => console.log(v));
source$.next("1");
s = null;
s = source$.subscribe(v => console.log(v));
source$.next("2");
s.unsubscribe();
Memory Leak
ngOnInit() {
  this.dataSub = this.getData().subscribe();
  this.cancelSub = fromEvent(
    cancelBtn, 
    'click',
  ).subscribe();
  this.rangeSub = fromEvent(
    rangeSelector, 
    'change',
  ).subscribe();
}
ngOnDestroy() {
  this.dataSub.unsubscribe();
  this.cancelSub.unsubscribe();
  this.rangeSub.unsubscribe();
}Unsubscribe manually in time
Take, TakeUntil
Complete
$destroyer = new Subject();
ngOnInit() {
  this.dataSub = this.getData().pipe(
    takeUntil(this.destroyer$),
  ).subscribe();
  
  this.cancelSub = fromEvent(
    cancelBtn, 
    'click',
  ).pipe(
  	takeUntil(this.destroyer$),
  ).subscribe();
  
  this.rangeSub = fromEvent(
    rangeSelector, 
    'change',
  ).pipe(
    takeUntil(this.destroyer$),
  ).subscribe();
}
ngOnDestroy() {
  this.$destroyer.next();
  this.$destroyer.complete();
  this.$destroyer.unsubscribe();
}import { Observable } from "rxjs";
import { switchMap, takeUntil } from "rxjs/operators";
declare const a: Observable<number>;
declare const b: Observable<number>;
declare const notifier: Observable<any>;
const c = a
  .pipe(
    takeUntil(notifier),
    switchMap((_) => b)
  )
  .subscribe((value) => console.log(value));TakeUntil Leak
RxJS Essential
By Junhao Zhang
RxJS Essential
- 172
 
   
   
  