以前在處理非同步的工作、資料傳遞(像是跟 server API 拿資料、等待結果回傳再進行下一步動作),常用的有 promise 和 async await,observable 跟他們最大的差異,就是能夠
- 實現 functional,取得 response 後可直接進行資料 parsing ,直觀好懂
- 實現 change listener,一旦訂閱,即可監聽未來所有數值的改變
RxJS observable 用起來像這樣:
export class DataStore {
public data: BehaviorSubject<number> = new BehaviorSubject(0);
update(newValue: number) { data.next(newValue); }
}
export class ButtonComponent {
public data: number;
constructor(private dataStore: DataStore) {
dataStore.data.subscribe(x => this.data = x);
}
onBtnClick() { this.dataStore.update(this.data + 1); }
}
由以上可知,我們在 DataStore
先設定了一個可供訂閱的 dataSubject
、初始值為 0。ButtonComponent
在 constructor
運作後就會訂閱 dataSubject
的所有變動,一旦變動就把最新的數值存到 data 裡面, 當按鈕按下去,會去呼叫 dataStore.update()
讓 dataSubject
存放的數值加一,進而觸發 data
改變。
這裡要注意到一點是,dataStore.dataSubject
只要有任何更新,所有訂閱他的元件都會拿取到最新的數值,這就是 observable 強大的地方,將所有與 dataStore
相關連的元件的資料溝通綁定在一起,不會再有一個動作改 A 元件結果沒改到 B 元件的情況。
Mobx observable 用起來像這樣:
export class DataStore {
@observable public data: number;
@action
update(newValue: number) { this.data = newValue; }
}
export class ButtonComponent {
public data: number;
constructor(private dataStore: DataStore) {
observe(dataStore, 'data', x => this.data = x.newValue);
}
onBtnClick() { this.dataStore.update(this.data + 1); }
}
看起來 Mobx 較為直觀好懂,因為他藉由 decorator @
讓變數有了可被觀測的能力。這裡注意一下我沒有使用 observe(dataStore.data, ...)
因為 observe
只支援觀測物件!如果要觀測 primitive value 要使用 observable box ,文章之後也會提到。
但接下來,將會遇到 Mobx 的弱點:
我們設定一個情境,要觀測資料開始 loading 與 loading完成的過程,加入isDataLoading
標記著資料開始改變到改變結束的狀態。
RxJS:
export class DataStore {
public dataSubject: BehaviorSubject<number> = new BehaviorSubject(0);
update(newValue: number) { dataSubject.next(newValue); }
}
export class ButtonComponent {
public data: number;
public isDataLoading: boolean;
constructor(private dataStore: DataStore) {
dataStore.dataSubject.subscribe(x => {
this.data = x;
this.isDataLoading = false;
});
}
onBtnClick() {
this.isDataLoading = true;
this.dataStore.update(this.data);
}
}
Mobx:
export class DataStore {
@observable public data:number;
@action
update(newValue: number) { this.data = newValue; }
}
export class ButtonComponent {
public data: number;
public isDataLoading: boolean;
constructor(private dataStore: DataStore) {
observe(dataStore, 'data', x => {
this.data = x.newValue;
this.isDataLoading = false;
});
}
onBtnClick() {
this.isDataLoading = true;
this.dataStore.update(this.data);
}
}
這裡會有個情況導致錯誤,observe
只有在 dataStore.data
改變時才會觸發,當我們執行 this.dataStore.update(this.data)
因為數值沒改變,observe
無法觸發,將導致 isDataLoading
一直為 true。
必須改寫成:
export class DataStore {
public get data(): number { return this.dataObserve.get(); }
public set data(v: number) { this.dataObserve.set(v); }
public dataObservable: IObservableValue<number> = observable.box(0);
update(newValue: number) { this.data = newValue; }
}
export class ButtonComponent {
public data: number;
public isDataLoading: boolean;
constructor(private dataStore: DataStore) {
observe(dataStore.dataObservable, x => {
this.data = x.newValue;
this.isDataLoading = false;
});
}
onBtnClick() {
this.isDataLoading = true;
this.dataStore.update(this.data);
}
}
這裡可看見 dataObservable
是個可觀測的 box 物件,便能使用observe(dataStore.dataObservable, ...)
並且不論數值是否更新,dataObserve.set(v)
都會觸發 observe
變動。
參考資料