今天咱們要講三個很是重要的 operators,這三個 operators 在不少的 RxJS 相關的 library 的使用示例上都會看到。不少初學者在使用這些 library 時,看到這三個 operators 極可能就放棄了,但其實若是有把這個系列的文章完整看過的話,如今應該就能很好接受跟理解。git
concatMap 其實就是 map 加上 concatAll 的簡化寫法,咱們直接來看一個示例github
var source = Rx.Observable.fromEvent(document.body, 'click');
var example = source
.map(e => Rx.Observable.interval(1000).take(3))
.concatAll();
example.subscribe({
next: (value) => { console.log(value); },
error: (err) => { console.log('Error: ' + err); },
complete: () => { console.log('complete'); }
});複製代碼
上面這個示例就能夠簡化成json
var source = Rx.Observable.fromEvent(document.body, 'click');
var example = source
.concatMap(
e => Rx.Observable.interval(100).take(3)
);
example.subscribe({
next: (value) => { console.log(value); },
error: (err) => { console.log('Error: ' + err); },
complete: () => { console.log('complete'); }
});複製代碼
先後兩個行爲是一致的,記得 concatMap 也會先處理前一個送出的 observable 在處理下一個 observable,畫成 Marble Diagram 以下promise
source : -----------c--c------------------...
concatMap(c => Rx.Observable.interval(100).take(3))
example: -------------0-1-2-0-1-2---------...複製代碼
這樣的行爲也很常被用在發送 request 以下瀏覽器
function getPostData() {
return fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(res => res.json())
}
var source = Rx.Observable.fromEvent(document.body, 'click');
var example = source.concatMap(
e => Rx.Observable.from(getPostData()));
example.subscribe({
next: (value) => { console.log(value); },
error: (err) => { console.log('Error: ' + err); },
complete: () => { console.log('complete'); }
});複製代碼
這裏咱們每點擊一下畫面就會送出一個 HTTP request,若是咱們快速的連續點擊,你們能夠在開發者工具的 network 看到每一個 request 是等到前一個 request 完成纔會送出下一個 request,以下圖ide
這裏建議把網速模擬調到最慢工具
從 network 的圖形能夠看得出來,第二個 request 的發送時間是接在第一個 request 以後的,咱們能夠確保每個 request 會等前一個 request 完成才作處理。post
concatMap 還有第二個參數是一個 selector callback,這個 callback 會傳入四個參數,分別是fetch
回傳值咱們想要的值,示例以下
function getPostData() {
return fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(res => res.json())
}
var source = Rx.Observable.fromEvent(document.body, 'click');
var example = source.concatMap(
e => Rx.Observable.from(getPostData()),
(e, res, eIndex, resIndex) => res.title);
example.subscribe({
next: (value) => { console.log(value); },
error: (err) => { console.log('Error: ' + err); },
complete: () => { console.log('complete'); }
});複製代碼
這個示例的外部 observable 送出的元素就是 click event 實例,內部 observable 送出的元素就是 response 實例,這裏咱們回傳 response 實例的 title 屬性,這樣一來咱們就能夠直接收到 title,這個方法很適合用在 response 要選取的值跟前一個事件或順位(index)相關時。
switchMap 其實就是 map 加上 switch 簡化的寫法,以下
var source = Rx.Observable.fromEvent(document.body, 'click');
var example = source
.map(e => Rx.Observable.interval(1000).take(3))
.switch();
example.subscribe({
next: (value) => { console.log(value); },
error: (err) => { console.log('Error: ' + err); },
complete: () => { console.log('complete'); }
});複製代碼
上面的代碼能夠簡化成
var source = Rx.Observable.fromEvent(document.body, 'click');
var example = source
.switchMap(
e => Rx.Observable.interval(100).take(3)
);
example.subscribe({
next: (value) => { console.log(value); },
error: (err) => { console.log('Error: ' + err); },
complete: () => { console.log('complete'); }
});複製代碼
畫成 Marble Diagram 表示以下
source : -----------c--c-----------------...
concatMap(c => Rx.Observable.interval(100).take(3))
example: -------------0--0-1-2-----------...複製代碼
只要注意一個重點 switchMap 會在下一個 observable 被送出後直接退訂前一個未處理完的 observable,這個部份的細節請看上一篇文章 switch 的部分。
另外咱們也能夠把 switchMap 用在發送 HTTP request
function getPostData() {
return fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(res => res.json())
}
var source = Rx.Observable.fromEvent(document.body, 'click');
var example = source.switchMap(
e => Rx.Observable.from(getPostData()));
example.subscribe({
next: (value) => { console.log(value); },
error: (err) => { console.log('Error: ' + err); },
complete: () => { console.log('complete'); }
});複製代碼
若是咱們快速的連續點擊五下,能夠在開發者工具的 network 看到每一個 request 會在點擊時發送,以下圖
灰色是瀏覽器原生地停頓行爲,實際上灰色的一開始就是 fetch 執行送出 request,只是卡在瀏覽器等待發送。
從上圖能夠看到,雖然咱們發送了多個 request 但最後真正印出來的 log 只會有一個,表明前面發送的 request 已經不會形成任何的 side-effect 了,這個很適合用在只看最後一次 request 的情境,好比說 自動完成(auto complete),咱們只須要顯示使用者最後一次打在畫面上的文字,來作建議選項而不用每一次的。
switchMap 跟 concatMap 同樣有第二個參數 selector callback 可用來回傳咱們要的值,這部分的行爲跟 concatMap 是同樣的,這裏就再也不贅述。
mergeMap 其實就是 map 加上 mergeAll 簡化的寫法,以下
var source = Rx.Observable.fromEvent(document.body, 'click');
var example = source
.map(e => Rx.Observable.interval(1000).take(3))
.mergeAll();
example.subscribe({
next: (value) => { console.log(value); },
error: (err) => { console.log('Error: ' + err); },
complete: () => { console.log('complete'); }
});複製代碼
上面的代碼能夠簡化成
var source = Rx.Observable.fromEvent(document.body, 'click');
var example = source
.mergeMap(
e => Rx.Observable.interval(100).take(3)
);
example.subscribe({
next: (value) => { console.log(value); },
error: (err) => { console.log('Error: ' + err); },
complete: () => { console.log('complete'); }
});複製代碼
畫成 Marble Diagram 表示
source : -----------c-c------------------...
concatMap(c => Rx.Observable.interval(100).take(3))
example: -------------0-(10)-(21)-2----------...複製代碼
記得 mergeMap 能夠並行處理多個 observable,以這個例子來講當咱們快速點按兩下,元素髮送的時間點是有機會重疊的,這個部份的細節你們能夠看上一篇文章 merge 的部分。
另外咱們也能夠把 switchMap 用在發送 HTTP request
function getPostData() {
return fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(res => res.json())
}
var source = Rx.Observable.fromEvent(document.body, 'click');
var example = source.mergeMap(
e => Rx.Observable.from(getPostData()));
example.subscribe({
next: (value) => { console.log(value); },
error: (err) => { console.log('Error: ' + err); },
complete: () => { console.log('complete'); }
});複製代碼
若是咱們快速的連續點擊五下,你們能夠在開發者工具的 network 看到每一個 request 會在點擊時發送而且會 log 出五個實例,以下圖
mergeMap 也能傳入第二個參數 selector callback,這個 selector callback 跟 concatMap 第二個參數也是徹底同樣的,但 mergeMap 的重點是咱們能夠傳入第三個參數,來限制並行處理的數量
function getPostData() {
return fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(res => res.json())
}
var source = Rx.Observable.fromEvent(document.body, 'click');
var example = source.mergeMap(
e => Rx.Observable.from(getPostData()),
(e, res, eIndex, resIndex) => res.title, 3);
example.subscribe({
next: (value) => { console.log(value); },
error: (err) => { console.log('Error: ' + err); },
complete: () => { console.log('complete'); }
});複製代碼
這裏咱們傳入 3 就能限制,HTTP request 最多隻能同時送出 3 個,而且要等其中一個完成在處理下一個,以下圖
你們能夠注意看上面這張圖,我連續點按了五下,但第四個 request 是在第一個完成後才送出的,這個很適合用在特殊的需求下,能夠限制同時發送的 request 數量。
RxJS 5 還保留了 mergeMap 的別名叫 flatMap,雖然官方文件上沒有,但這兩個方法是徹底同樣的。請參考這裏
這三個 operators 還有一個共同的特性,那就是這三個 operators 能夠把第一個參數所回傳的 promise 實例直接轉成 observable,這樣咱們就不用再用 Rx.Observable.from
轉一次,以下
function getPersonData() {
return fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(res => res.json())
}
var source = Rx.Observable.fromEvent(document.body, 'click');
var example = source.concatMap(e => getPersonData());
//直接回傳 promise 實例
example.subscribe({
next: (value) => { console.log(value); },
error: (err) => { console.log('Error: ' + err); },
complete: () => { console.log('complete'); }
});複製代碼
至於在使用上要如何選擇這三個 operators? 其實都仍是看使用情境而定,這裏筆者簡單列一下大部分的使用情境
建議初學者不肯定選哪個時,使用 switchMap
在使用 concatAll 或 concatMap 時,請注意內部的 observable 必定要可以的結束,且外部的 observable 發送元素的速度不能比內部的 observable 結束時間快太多,否則會有 memory issues
今天的文章內容主要講了三個 operators,若是有看完上一篇文章的讀者應該不難吸取,主要仍是使用情境上須要思考以及注意一些細節。
不知道今天讀者有沒有收穫呢? 若是有任何問題,歡迎留言給我,謝謝