這是【30天精通 RxJS】的 07 篇,如果還沒看過 06 篇可以往這邊走:
30 天精通 RxJS (06): 建立 Observable(二)
昨天我們把所有建立 Observable 實例的 operators 講完了,接下來我們要講關(guān)于轉(zhuǎn)換(Transformation)、過濾(Filter)、合併(Combination)等操作方法。先來讓我們看看什麼是 Operator
什麼是 Operator?
Operators 就是一個個被附加到 Observable 型別的函式,例如像是 map, filter, contactAll... 等等,所有這些函式都會拿到原本的 observable 并回傳一個新的 observable,就像有點像下面這個樣子
var people = Rx.Observable.of('Jerry', 'Anna');
function map(source, callback) {
return Rx.Observable.create((observer) => {
return source.subscribe(
(value) => {
try{
observer.next(callback(value));
} catch(e) {
observer.error(e);
}
},
(err) => { observer.error(err); },
() => { observer.complete() }
)
})
}
var helloPeople = map(people, (item) => item + ' Hello~');
helloPeople.subscribe(console.log);
// Jerry Hello~
// Anna Hello~
這裡可以看到我們寫了一個 map 的函式,它接收了兩個參數(shù),第一個是原本的 observable,第二個是 map 的 callback function。map 內(nèi)部第一件事就是用 create 建立一個新的 observable 并回傳,并且在內(nèi)部訂閱原本的 observable。
當(dāng)然我們也可以直接把 map 塞到 Observable.prototype
function map(callback) {
return Rx.Observable.create((observer) => {
return this.subscribe(
(value) => {
try{
observer.next(callback(value));
} catch(e) {
observer.error(e);
}
},
(err) => { observer.error(err); },
() => { observer.complete() }
)
})
}
Rx.Observable.prototype.map = map;
var people = Rx.Observable.of('Jerry', 'Anna');
var helloPeople = people.map((item) => item + ' Hello~');
helloPeople.subscribe(console.log);
// Jerry Hello~
// Anna Hello~
這裡有兩個重點是我們一定要知道的,每個 operator 都會回傳一個新的 observable,而我們可以透過 create 的方法建立各種 operator。
在 RxJS 5 的實作中,其實每個 operator 是透過原來 observable 的 lift 方法來建立新的 observable,這個方法會在新回傳的 observable 物件內(nèi)偷塞兩個屬性,分別是 source 與 operator,記錄原本的資料源跟當(dāng)前使用的 operator。
其實 lift 方法還是用 new Observable(跟 create 一樣)。至于為什麼要獨立出這個方法,除了更好的封裝以外,主要的原因是為了讓 RxJS 5 的使用者能更好的 debug。關(guān)于 RxJS 5 的除錯方式,我們會專門寫一篇來講解!
這裡我們只是簡單的實作 operator。如果之后實務(wù)上,想要不影響原本的 Observable 又能夠自訂 operator 可以參考官方的這份文件。(現(xiàn)在先不用看)
其實 RxJS 提供的各種 operators 已經(jīng)非常夠用了,不太需要我們自己創(chuàng)造 operator,這裡只是想讓大家先對 operator 的建立有個基本的觀念,之后在學(xué)習(xí)的過程中會比較輕鬆。
在我們開始介紹 RxJS 的 operators 前,為了能讓我們更好地理解各種 operators,我們需要先訂定一個簡單的方式來表達(dá) observable!
Marble diagrams
我們在傳達(dá)事物時,文字其實是最糟的手段,雖然文字是我們平時溝通的基礎(chǔ),但常常千言萬語也比不過一張清楚的圖。如果我們能訂定 observable 的圖示,就能讓我們更方便的溝通及理解 observable 的各種 operators!
我們把描繪 observable 的圖示稱為 Marble diagrams,在網(wǎng)路上 RxJS 有非常多的 Marble diagrams,規(guī)則大致上都是相同的,這裡為了方便撰寫以及跟讀者的留言互動,所以採用類似 ASCII 的繪畫方式。
我們用 - 來表達(dá)一小段時間,這些 - 串起就代表一個 observable。
----------------
X (大寫 X)則代表有錯誤發(fā)生
---------------X
| 則代表 observable 結(jié)束
----------------|
在這個時間序當(dāng)中,我們可能會發(fā)送出值(value),如果值是數(shù)字則直接用阿拉伯?dāng)?shù)字取代,其他的資料型別則用相近的英文符號代表,這裡我們用 interval 舉例
var source = Rx.Observable.interval(1000);
source 的圖形就會長像這樣
-----0-----1-----2-----3--...
當(dāng) observable 是同步送值的時候,例如
var source = Rx.Observable.of(1,2,3,4);
source 的圖形就會長像這樣
(1234)|
小括號代表著同步發(fā)生。
另外的 Marble diagrams 也能夠表達(dá) operator 的前后轉(zhuǎn)換,例如
var source = Rx.Observable.interval(1000);
var newest = source.map(x => x + 1);
這時 Marble diagrams 就會長像這樣
source: -----0-----1-----2-----3--...
map(x => x + 1)
newest: -----1-----2-----3-----4--...
最上面是原本的 observable,中間是 operator,下面則是新的 observable。
以上就是 Marble diagrams 如何表示 operator 對 observable 的操作,這能讓我們更好的理解各個 operator。
Marble Diagrams 相關(guān)資源:http://rxmarbles.com/
最后讓我們來看幾個簡單的 Operators!
Operators
map
Observable 的 map 方法使用上跟陣列的 map 是一樣的,我們傳入一個 callback function,這個 callback function 會帶入每次發(fā)送出來的元素,然后我們回傳新的元素,如下
var source = Rx.Observable.interval(1000);
var newest = source.map(x => x + 2);
newest.subscribe(console.log);
// 2
// 3
// 4
// 5..
用 Marble diagrams 表達(dá)就是
source: -----0-----1-----2-----3--...
map(x => x + 1)
newest: -----1-----2-----3-----4--...
我們有另外一個方法跟 map 很像,叫 mapTo
mapTo
mapTo 可以把傳進(jìn)來的值改成一個固定的值,如下
var source = Rx.Observable.interval(1000);
var newest = source.mapTo(2);
newest.subscribe(console.log);
// 2
// 2
// 2
// 2..
mapTo 用 Marble diagrams 表達(dá)
source: -----0-----1-----2-----3--...
mapTo(2)
newest: -----2-----2-----2-----2--...
filter
filter 在使用上也跟陣列的相同,我們要傳入一個 callback function,這個 function 會傳入每個被送出的元素,并且回傳一個 boolean 值,如果為 true 的話就會保留,如果為 false 就會被濾掉,如下
var source = Rx.Observable.interval(1000);
var newest = source.filter(x => x % 2 === 0);
newest.subscribe(console.log);
// 0
// 2
// 4
// 6..
filter 用 Marble diagrams 表達(dá)
source: -----0-----1-----2-----3-----4-...
filter(x => x % 2 === 0)
newest: -----0-----------2-----------4-...
讀者應(yīng)該有發(fā)現(xiàn) map, filter 這些方法其實都跟陣列的相同,因為這些都是 functional programming 的通用函式,就算換個語言也有機(jī)會看到相同的命名及相同的用法。
實際上 Observable 跟 Array 的 operators(map, filter),在行為上還是有極大的差異。當(dāng)我們的資料量很大時,Observable 的效能會好上非常多。我們會有一天專門講這個部份!
今日小結(jié)
今天我們講了 Observable Operators 的相關(guān)知識,有以下幾個重點
- 什麼是 Operators
- 如何建立 operator
- Marble diagrams
- Operators
- map
- mapTo
- filter
不知道今天讀者有沒有收穫呢?歡迎在下方留言給我,這是精通 RxJS 的第 07 篇!