RxJS mergeMap和switchMap

假設現(xiàn)在有一個簡單的任務:頁面上有一個按鈕,當你點擊按鈕的時候,需要啟動一個定時器。使用 RxJS 我們可以可以很方便地實現(xiàn)上述功能:

雖然以上代碼能夠正常運行,但仍存在兩個問題:

存在類似于回調地域的問題。
我們必須手動處理每個訂閱。

接下來讓我們來介紹一下高階 observable 及如何利用它使得事情變得更簡單。
高階 Observables

一個 Observable 對象可以發(fā)出任何類型的值:數(shù)值、字符串、對象等等。這意味著 Observable 對象也可以發(fā)出 Observable 類型的值。

與 JavaScript 高階函數(shù)類似,一個高階的 Observable 表示一個 Observable 對象內部會返回另一個 Observable 對象。此時我們來更新一下上面的示例,以便更加直觀的了解上述概念:

import { fromEvent, interval } from 'rxjs';
import { map } from 'rxjs/operators';

const button = document.querySelector('button');

const click= fromEvent(button, 'click'); const interval = interval(1000);

const clicksToInterval= click.pipe(map(event => {
return interval$;
}));

clicksToInterval$.subscribe(intervalObservable =>
console.log(intervalObservable)
);

當用戶點擊按鈕時,我們的 map 操作符將返回一個 interval observable 對象。當我們訂閱 clicksToInterval$ 對象時,將發(fā)出 intervalObservable 對象。

在你訂閱 clicksToInterval$ 對象時,控制臺輸出的是 intervalObservable 對象。這里需要記住的是,observable 對象是 lazy 的,如果想要從一個 observable 對象中獲取值,你必須執(zhí)行訂閱操作,比如:

clicksToInterval$.subscribe(intervalObservable => {
intervalObservable.subscribe(num => {
console.log(num);
});
});

在介紹高階 observable 對象的概念之后,接下來讓我們來介紹兩個有用的操作符,用來幫助我們解決上面提到的問題。
mergeAll

When the inner observable emits, let me know by merging the value to the outer observable.

mergeAll() 操作符底層做的操作跟上面的例子一樣,它獲取 inner observable 對象,執(zhí)行訂閱操作,然后把值推給 observer (觀察者)對象。

import { fromEvent, interval } from 'rxjs';
import { map, mergeAll } from 'rxjs/operators';

const click= fromEvent(button, 'click'); const interval = interval(1000);

const observable= click.pipe(map(event => {
return interval$;
}));

observable$.mergeAll().subscribe(num => console.log(num));

在上面的示例中,source observable 對象是 clicksobservable 對象,而 inner observable 對象是 interval observable 對象。

在 RxJS 中這是一個通用的模式,因此有一個快捷方式來實現(xiàn)相同的行為 —— mergeMap():

mergeMap() <=> map() + mergeAll()

const button = document.querySelector('button');

const click= fromEvent(button, 'click'); const interval = interval(1000);

const observable= click.pipe(mergeMap(event => {
return interval$;
}));

observable$.subscribe(num => console.log(num));

在上面的代碼中,每當我們點擊按鈕,我們都會調用 interval$ 對象的 subscribe() 方法,這將導致在我們的頁面中會存在多個獨立的定時器。如果這是你期望實現(xiàn)的功能,那就沒問題。但如果你只想保持一個數(shù)據(jù)源,你就需要使用 switch() 操作符。
switch

Like mergeMap() but when the source observable emits cancel any previous subscriptions of the inner observable.

switch() 用于取消前一個訂閱,并切換至新的訂閱。如果我們把代碼更新為 switch() 操作符,當我們多次點擊按鈕時,我們可以看到每次點擊按鈕時,我們將獲取新的 interval 對象,而上一個 interval 對象將會被自動取消。

const button = document.querySelector('button');

const click= fromEvent(button, 'click'); const interval = interval(1000);

const observable= click.pipe(
map(event => {
return interval$;
}),
switchAll()
);

observable$.subscribe(num => console.log(num));

正如你說看到的,當點擊三次按鈕后,我們僅有一個 interval 對象。反之,使用 merge() 操作符,我們會有三個獨立的 interval 對象。當源發(fā)出新值后,switch 操作符會對上一個內部的訂閱對象執(zhí)行取消訂閱操作。

在 RxJS 中這也是一個通用的模式,因此也有一個快捷方式來實現(xiàn)相同的行為 —— switchMap():

switchMap() <=> map() + switch()

const button = document.querySelector('button');

const click= fromEvent(button, 'click'); const interval = interval(1000);

const observable= click.pipe(
switchMap(event => {
return interval$;
})
);

observable$.subscribe(num => console.log(num));

本文由博客一文多發(fā)平臺 OpenWrite 發(fā)布!

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容