achieve
此篇內容做為個人學習記錄,不會再更改。
學習 haskell後,發現更重要的其實是函式編程的思考方式。
如果真的理解函式做為一級公民的思考邏輯, rxjs 的實現看起來就會非常自然、直覺。
當初寫這篇學習記錄時,並不懂fp的思考方式。
因此,下面的記錄比較像是逐行記錄動作、及 ts特性,並沒有真正解釋 rxjs 的實作目的。
2020/09/16
目的:看懂Rxjs的Observable實現方式
Rxjs的流程圖
Rxjs是一個實作了 觀察者模式、疊代器模式的library。
根據 ReactiveX 官網所述:
Observables fill the gap by being the ideal way to access asynchronous sequences of multiple items
| # | single items | multiple items |
|---|---|---|
| synchronus | T getData() |
Iterable<T> getData() |
| asynchronous | Future<T> getData() |
Observable<T> getData() |
ReactiveX 填補了疊代器模式無法支援非同步旨令的問題。
事實上,我在理解原始碼的時候有發現,雖然Rxjs的確有實現 觀察者模式,但是當你在看程式碼的時候,將它當作一個變型的疊代器模式,反而更容易理解。
那麼我們就先從一個流程圖及簡單的範例開始吧。
以下是我們使用 Rxjs的時候,最典型的流程。
我直接從 Anuglar 官網引用簡單的範例:
// Create simple observable that emits three values
const myObservable = 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);
// Logs:
// Observer got a next value: 1
// Observer got a next value: 2
// Observer got a next value: 3
// Observer got a complete notificationcode 1使用of運算子產生Observable並訂閱。
另外,如果你不想要使用 Operator 建立 Observable的話,你可以直接 new一個 Obsevrable 實例,並且將訂閱後會發出值使用 callback function 的方式傳入 Observable 中。
實務上是不會這麼使用的。在實務上,可以使用
of這類的 creation operator 產生一個最簡單的Observable。而其內部的實現方式,其實就是回傳一個會固定發出of參數的Observable,
// This function runs when subscribe() is called
function sequenceSubscriber(observer) {
// synchronously deliver 1, 2, and 3, then complete
observer.next(1);
observer.next(2);
observer.next(3);
observer.complete();
// unsubscribe function doesn't need to do anything in this
// because values are delivered synchronously
return {unsubscribe() {}};
}
// Create a new Observable that will deliver the above sequence
const sequence = new Observable(sequenceSubscriber);
// execute the Observable and print the result of each notification
sequence.subscribe({
next(num) { console.log(num); },
complete() { console.log('Finished sequence'); }
});
// Logs:
// 1
// 2
// 3
// Finished sequencecode 2 建立 一個 Observable 物件。
這樣我們已經知道最基本的使用方式了。接著我們看看 Rxjs 中最核心的兩個組件:Observable 與 Observer。
Observable
Observable 是 Rxjs 中最核心的物件。
我剛開始看Observable的實現方式時,旁邊放著 觀察者模式 的 類別圖。
然後我看的很困惑。所以我不建議使用觀察者模式對應Rxjs實作。
截取重要的幾段程式碼:
export class Observable<T> implements Subscribable<T> {
/** Internal implementation detail, do not use directly. */
public _isScalar: boolean = false;
/** @deprecated This is an internal implementation detail, do not use. */
source: Observable<any> | undefined;
/** @deprecated This is an internal implementation detail, do not use. */
operator: Operator<any, T> | undefined;
/**
* @constructor
* @param {Function} subscribe the function that is called when the Observable is
* initially subscribed to. This function is given a Subscriber, to which new values
* can be `next`ed, or an `error` method can be called to raise an error, or
* `complete` can be called to notify of a successful completion.
*/
constructor(subscribe?: (this: Observable<T>, subscriber: Subscriber<T>) => TeardownLogic) {
if (subscribe) {
this._subscribe = subscribe;
}
}
// .... 中間略
subscribe(observer?: PartialObserver<T>): Subscription;
/** @deprecated Use an observer instead of a complete callback */
subscribe(next: null | undefined, error: null | undefined, complete: () => void): Subscription;
/** @deprecated Use an observer instead of an error callback */
subscribe(next: null | undefined, error: (error: any) => void, complete?: () => void): Subscription;
/** @deprecated Use an observer instead of a complete callback */
subscribe(next: (value: T) => void, error: null | undefined, complete: () => void): Subscription;
subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): Subscription;
subscribe(observerOrNext?: PartialObserver<T> | ((value: T) => void) | null,
error?: ((error: any) => void) | null,
complete?: (() => void) | null): Subscription {
const { operator } = this;
const sink = toSubscriber(observerOrNext, error, complete);
if (operator) {
sink.add(operator.call(sink, this.source));
} else {
sink.add(
this.source || (config.useDeprecatedSynchronousErrorHandling && !sink.syncErrorThrowable) ?
this._subscribe(sink) :
this._trySubscribe(sink)
);
}
if (config.useDeprecatedSynchronousErrorHandling) {
if (sink.syncErrorThrowable) {
sink.syncErrorThrowable = false;
if (sink.syncErrorThrown) {
throw sink.syncErrorValue;
}
}
}
return sink;
}
// ... (略)
}code 3.Observable程式碼。
不論 pipe 與 Operator 的話,整個 Observable的重點,其實就是 subscribe函式。
Ts 筆記
code 3 中,建構子使用了
function type的寫法(第19行),定義了 傳入的 function 的參數及 return 的型態。 另外,function type的第一個參數可以放this,它可以限制function的this型態。 (因此,此函式不能使用arrow function)底線是一個常用的命名規則(21行,_subscribe屬性),代表變數或屬性是內部屬性。
subscribe使用了ts函式多載的功能(27行 ~ 36行)。ts支援函式多載, 唯一的限制是,參數的個數必須要相同。 因此可以注意到,subscibe的所有參數都是optional parameter。跟
java等語言不一樣。 ts 底層其實還是js。因此與其說它是函式多載,不如說它可以定義多個函式簽名。 在 copmile階段,編譯器會選擇最適合的函式簽名來檢查。 refer to@deprecated 是軟體中很常使用的標籤(38行),代表被棄用的function。不論是公開的 Api 或者是 內部的 Api 都非常的好用。
看到 Subscribe 的第一一個參數,它可以是一個 PartialObserver<T> 或者是一個 function。
看一下 PartialObserver
export interface NextObserver<T> {
closed?: boolean;
next: (value: T) => void;
error?: (err: any) => void;
complete?: () => void;
}
export interface ErrorObserver<T> {
closed?: boolean;
next?: (value: T) => void;
error: (err: any) => void;
complete?: () => void;
}
export interface CompletionObserver<T> {
closed?: boolean;
next?: (value: T) => void;
error?: (err: any) => void;
complete: () => void;
}
export type PartialObserver<T> = NextObserver<T> | ErrorObserver<T> | CompletionObserver<T>;code 4. Observer interface
其實,PartialObserver 就是 Observer介面的別名,只是三個function中,你至少要定義其中一個。
Observer 及 Subscriber
Observer 其實是 Rxjs定義的界面。
定義界面的好處之一,是可以規範程式的 input 的規格。
export interface Observer<T> {
closed?: boolean;
next: (value: T) => void;
error: (err: any) => void;
complete: () => void;
}code 5、Observer
另外,Subscriber 是個物件,它實現了 Observer 、並且繼承了 Subscription。現在只要知道,Subscriber 多了 取消訂閱的能力就好 (unsubscribe() 方法)。
我們繼續看 code 3 。
code 3第19行~23行。它的建構子其實很單純,就是把傳入的Subscriber function 放入一個內部的 _subscribe屬性。這部份的使用案例可以對應 code 2 看。
接著看到 subscribe() 函式。
code 3,41行。首先會透過 toSubscriber() 這個 utils 將 傳入的 Observer轉變成 Subscriber 。
前面提到,在 Rxjs的內部實現中,會把所有的 Observer界面,轉化為 Subscriber 物件,提供訂閱及取消訂閱的能力。
utils 放全域的 function,也是軟體中很常見的專案架構(code 3,41行)。
code 3,43~51行。接著,如果你是透過 creation operator 產生的 observable,subscribe()函式會呼叫這個 Operator(對應 code 1。
如果不是的話,會試著去執行 _subscribe (對應 code2)。
code 3,53~60行。最下面的 if、else,控制 throw exception 。因為新版建議使用 Observer界面,取代將 subscribe 包在 try...catch中,因此這裡會檢查lib的 config檔的flag有沒有被打開;
如果有的話,再檢查是否有遇到例外需要throw回主函式的情型。
結論
從 Observable 的 subscribe() 中,可以了解到,Obsrevable的 subscribe() 要發出的值有兩個來源:要馬是你 new 一個 Observable,並且把 subscribe、及 unsubscribe 的邏輯傳入;要馬是使用 Operator 來發出值。
使用情境來看,後者應該是更常見的情境。
另外,內部會將Observer轉成 Subscriber,是非常重要的一步。有了它,它有辦法實作 pipe。
觀察者模式,通常是異步的,也不會有 Observable 、Operator等物件。因此我開頭才說,最好不要用 觀察者模式 對應了解Rxjs的實作,否則只會越看越糊塗。
以目前看到的行為來說,Observable 更接近 疊代器模式。