RxJS mergeMap和switchMap

假设现在有一个简单的任务:页面上有一个按钮,当你点击按钮的时候,需要启动一个定时器。使用 RxJS 我们可以可以很方便地实现上述功能:

虽然以上代码能够正常运行,但仍存在两个问题:

存在类似于回调地域的问题。
我们必须手动处理每个订阅。

接下来让我们来介绍一下高阶 observable 及如何利用它使得事情变得更简单。
高阶 Observables

一个 Observable 对象可以发出任何类型的值:数值、字符串、对象等等。这意味着 Observable 对象也可以发出 Observable 类型的值。

与 JavaScript 高阶函数类似,一个高阶的 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$ 对象时,将发出 intervalObservable 对象。

在你订阅 clicksToInterval$ 对象时,控制台输出的是 intervalObservable 对象。这里需要记住的是,observable 对象是 lazy 的,如果想要从一个 observable 对象中获取值,你必须执行订阅操作,比如:

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 对象,执行订阅操作,然后把值推给 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 中这是一个通用的模式,因此有一个快捷方式来实现相同的行为 —— 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() 方法,这将导致在我们的页面中会存在多个独立的定时器。如果这是你期望实现的功能,那就没问题。但如果你只想保持一个数据源,你就需要使用 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 对象。当源发出新值后,switch 操作符会对上一个内部的订阅对象执行取消订阅操作。

在 RxJS 中这也是一个通用的模式,因此也有一个快捷方式来实现相同的行为 —— 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));

本文由博客一文多发平台 OpenWrite 发布!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容