小章魚觀察日記
RxJS
Jonny Huang
jonnyhuang@outlook.com
偽基百科:三眼章魚
特徵一
有三隻眼睛
-
next
-
error
-
complete
特徵二
很喜歡觀賞裙內風光,總是把頭鑽到別人的褲襠裡。
特徵三
不喜歡別人把頭探進自己裙子裡,發起脾氣的時候她就會踹下面的眼睛,主動警告它
因為它喜歡用眼睛觀察別人,所以我們叫它Observer(觀察者)
有穿裙子可以被別人看的,我們叫它
Observable (可觀察對象)
當然 Observable 也具有 Observer 的特性
Observer & Observable
大部分最後一個是 Observer,因為我們只需要觀察最終結果。
end
// Create simple observable that emits three values
const myObservable = Observable.of(1, 2, 3);
// Create observer object
const myObserver = {
next: x => console.log('Observer got a next value: ' + x),
error: err => console.error('Observer got an error: ' + err),
complete: () => console.log('Observer got a complete notification'),
};
// Execute with the observer object
myObservable.subscribe(myObserver);
map
生氣時機:上面踹它的時候
作法:踹下面同一隻眼睛,順帶加點料
map
var x
x = 1
x = 2
x = 3
var y
y = 10
y = 20
y = 30
complet
complet
彈珠圖
var x
x = 1
x = 2
x = 3
var y
y = 10
y = 20
y = 30
complet
complet
彈珠圖
filter
生氣時機:踐踏了它的原則
作法:踹下面同一隻眼睛
filter
single
生氣時機:不小心只能有一次
作法:踹下面紅眼睛(error)
single
export class Observable<T> implements Subscribable<T> {
}
export interface Subscribable<T> {
subscribe(observerOrNext?: PartialObserver<T> | ((value: T) => void),
error?: (error: any) => void,
complete?: () => void): Unsubscribable;
}
Observable 關心的重點是連結(subscribe、unsubscribable)
Observable
Observer
Observable
export function map<T, R>(project: (value: T, index: number) => R, thisArg?: any): OperatorFunction<T, R> {
return function mapOperation(source: Observable<T>): Observable<R> {
if (typeof project !== 'function') {
throw new TypeError('argument is not a function. Are you looking for `mapTo()`?');
}
return source.lift(new MapOperator(project, thisArg));
};
}
export class Observable<T> implements Subscribable<T> {
lift<R>(operator: Operator<T, R>): Observable<R> {
const observable = new Observable<R>();
observable.source = this;
observable.operator = operator;
return observable;
}
}
Observable
source:observable
destination:MapOperator
Observable 連接方式比較像俄羅斯套娃
export class MapOperator<T, R> implements Operator<T, R> {
constructor(private project: (value: T, index: number) => R, private thisArg: any) {
}
call(subscriber: Subscriber<R>, source: any): any {
return source.subscribe(new MapSubscriber(subscriber, this.project, this.thisArg));
}
}
Operator 其實只負責 訂閱(subscribe)
Subscriber
Subscriber
Observable
Observable
class MapSubscriber<T, R> extends Subscriber<T> {
}
export class Subscriber<T> extends Subscription implements Observer<T> {
}
export interface Observer<T> {
closed?: boolean;
next: (value: T) => void;
error: (err: any) => void;
complete: () => void;
}
Subscriber 其實也是 Observer,Observer 只是 interface
Observer
next()
error()
complete()
Observer
next()
error()
complete()
export class Subscriber<T> extends Subscription implements Observer<T> {
protected isStopped: boolean = false;
next(value?: T): void {
if (!this.isStopped) {
this._next(value);
}
}
error(err?: any): void {
if (!this.isStopped) {
this.isStopped = true;
this._error(err);
}
}
complete(): void {
if (!this.isStopped) {
this.isStopped = true;
this._complete();
}
}
unsubscribe(): void {
if (this.closed) {
return;
}
this.isStopped = true;
super.unsubscribe();
}
}
Subscriber 可以說是 Observer 介面的實作。
一但執行過 error() 或是 complete(),isStopped 會被設定成 true,
next() 就不會再運作。
protected _next(value: T): void {
this.destination.next(value);
}
protected _error(err: any): void {
this.destination.error(err);
this.unsubscribe();
}
protected _complete(): void {
this.destination.complete();
this.unsubscribe();
}
class MapSubscriber<T, R> extends Subscriber<T> {
count: number = 0;
private thisArg: any;
constructor(destination: Subscriber<R>,
private project: (value: T, index: number) => R,
thisArg: any) {
super(destination);
this.thisArg = thisArg || this;
}
protected _next(value: T) {
let result: any;
try {
result = this.project.call(this.thisArg, value, this.count++);
} catch (err) {
this.destination.error(err);
return;
}
this.destination.next(result);
}
}
其實我們也可以自己實作 RxJS 的 Operator
繼承 Subscriber 並依需求複寫
_next()、_error()、_complete()
class SingleSubscriber<T> extends Subscriber<T> {
private seenValue: boolean = false;
private singleValue: T;
private index: number = 0;
private applySingleValue(value: T): void {
if (this.seenValue) {
this.destination.error('Sequence contains more than one element');
} else {
this.seenValue = true;
this.singleValue = value;
}
}
protected _next(value: T): void {
const index = this.index++;
if (this.predicate) {
this.tryNext(value, index);
} else {
this.applySingleValue(value);
}
}
}
next() 並不一定會呼叫後面的 next()
Single 在第2次呼叫 next()
會改呼叫下一個 Observer 的 error()
做事的只有 Subscriber
- 傳值過來時要做什麼?
- 傳錯誤過來時要做什麼?
- 通知完成時要做什麼?
- 自己做完事情之後是否要通知後面的 Subscriber?以及通知什麼?
class RetrySubscriber<T> extends Subscriber<T> {
constructor(destination: Subscriber<any>,
private count: number,
private source: Observable<T>) {
super(destination);
}
error(err: any) {
if (!this.isStopped) {
const { source, count } = this;
if (count === 0) {
return super.error(err);
} else if (count > -1) {
this.count = count - 1;
}
source.subscribe(this._unsubscribeAndRecycle());
}
}
}
retry 是複寫 error
retry :為什麼發生錯誤(error)後還可以繼續通知?
先捨棄掉再重新訂閱
class RepeatSubscriber<T> extends Subscriber<T> {
constructor(destination: Subscriber<any>,
private count: number,
private source: Observable<T>) {
super(destination);
}
complete() {
if (!this.isStopped) {
const { source, count } = this;
if (count === 0) {
return super.complete();
} else if (count > -1) {
this.count = count - 1;
}
source.subscribe(this._unsubscribeAndRecycle());
}
}
}
retry 是複寫 complete
那 repeat 呢?
先捨棄掉再重新訂閱
Q:Observable 重複訂閱會發生什麼事情?
export class Subject<T> extends Observable<T> implements SubscriptionLike {
observers: Observer<T>[] = [];
next(value?: T) {
if (!this.isStopped) {
...
for (let i = 0; i < len; i++) {
copy[i].next(value);
}
}
}
error(err: any) {
...
for (let i = 0; i < len; i++) {
copy[i].error(err);
}
this.observers.length = 0;
}
complete() {
...
for (let i = 0; i < len; i++) {
copy[i].complete();
}
this.observers.length = 0;
}
}
Q:Subject 如何多播給多個觀察者?
Subject 與 Observable 另一個差異就是它對外提供 next()、error()、complete() 三個方法,讓我們可以事後在變更值。
換個角度
認真你就輸了
用 SQL 邏輯來思考
-
FROM
-
JOIN
-
WHERE
-
SELECT
-
DISTINCT
-
ORDER BY
-
TOP
資料:create、from、of
篩選:filter
合併:merge、concat
轉換:map
過濾:distinct
排序:
過濾:first、last、skip、take
rxjs_2018_06_11
By Jonny Huang
rxjs_2018_06_11
- 2,084