ChatGPT解决这个技术问题 Extra ChatGPT

rxjs 中的 flatMap、mergeMap、switchMap 和 concatMap?

有人,请用Javascript解释SwitchMap和FlatMap之间的区别(从角度来看,rxjs 5)

在我的理解中。

SwitchMap 只发出最新的 observable 值并取消之前的 observable。

flatMap 收集所有单独的 observables 并在一个数组中返回所有 observables,而不关心 observable 的顺序。异步工作。

concatMap 保留顺序并发出所有可观察值,同步工作

那正确吗?

mergeMap 与上面的工作方式有何不同?

有人,请举例说明。

flatMapmergeMapan alias

b
blacktide

取自 previous answer

flatMap/mergeMap - 立即为任何源项目创建一个 Observable,所有以前的 Observable 都保持活动状态。注意 flatMap 是 mergeMap 的别名,而 flatMap 将在 RxJS 8 中被移除。

concatMap - 在创建下一个 Observable 之前等待前一个 Observable 完成

switchMap - 对于任何源项,完成前一个 Observable 并立即创建下一个

excludeMap - 源项目在前一个 Observable 未完成时被忽略

下面是一个示例,说明当源是直接项 (0,1,2,3,4) 并且 map 函数创建一个将每个项延迟 500 毫秒的 Observable 时,每个运算符的行为方式:

const { mergeMap, flatMap, concatMap, switchMap, exhaustMap } = Rx.operators;常量示例 = operator => () => Rx.Observable.from([0,1,2,3,4]) .pipe( operator(x => Rx.Observable.of(x).delay(500)) ) .subscribe(console.log, () => {}, () => console.log(`${operator.name} 完成`));常量 mm = 示例(合并映射);常量 fm = 示例(平面地图);常量 cm = 示例(concatMap);常量 sm = 示例(switchMap);常量 em = 示例(exhaustMap); .examples > div { 光标:指针;背景颜色:#4CAF50;白颜色;填充:7px 16px;显示:内联块; }

mergeMap
flatMap
concatMap
switchMap< /div>
exhaustMap


非常感谢你提供了这样一个美丽的例子。
令人难以置信的答案,喜欢这个片段。
当您单击“flatMap”时,它会发出“mergeMap completed” - 错误或错字?
为什么任何文档都不能这么简洁?总是那么无聊,所以我在读完下一个之前忘记了前一个是如何工作的。
@MarcinPevik flatMapmergeMap 的别名,这就是它打印出 mergeMap 的原因。 flatMap 将在 v8 中从 RxJS 中删除。
k
kos

在下面的弹珠图中,以 5ms10ms20ms 发出的源流将 *映射timer(0, 3),限制为 3 次排放:

https://i.stack.imgur.com/zNfwS.png

在这里玩这个大理石图:"mergeMap vs exhaustMap vs switchMap vs concatMap"

已经有了所有这些很棒的答案,我想添加一个更直观的解释

希望它可以帮助某人


弹珠是 imo 与 observables 的最佳方式。很多这些答案对某些人来说毫无意义
C
Community

@ZahiC,很酷的答案 - 我喜欢在代码示例中使用功能组合。如果可以的话,我想借用它来说明一些使用定时可观察的额外点。

外部、内部和控制

这些操作符都是 transformation operatorsmap() 一样,共同的特点是它们有一个 outerinner observable。关键区别在于外部可观察对象控制内部可观察对象的方式。

为了对比它们,我的代码示例成对运行它们,以 [outerValue,innerValue] 的形式输出值。我在测试中添加了间隔,并更改了内部延迟,以便在时间上有一些重叠(使用的公式是 delay((5-x)*200))。

合并地图与连接地图

这两者都输出所有值,不同之处在于排序。

mergeMap - 按内部 observable [0,0],[1,0],[0,1],[2,0],[1,1],[3,0],[2,1],[4 排序,0],[3,1],[4,1] concatMap - 按外部 observable [0,0],[0,1],[1,0],[1,1],[2,0] 排序,[2,1],[3,0],[3,1],[4,0],[4,1]

从输出来看,mergeMap 的外发射可以按顺序延迟,但 concatMap 遵循严格的外发射顺序。

switchMap 与排气映射

这些都限制了输出。

switchMap - 最后 [3,0],[4,0],[4,1] 节流

从输出中,switchMap 会限制任何不完整的内部发射,但排气映射会限制后续发射,直到较早的发射完成。

合并地图与开关地图

我把它扔进去是因为 switchmap 经常用在真正应该使用 mergeMap 的 SO 答案中。

mergeMap - 按内部 observable [0,0],[1,0],[0,1],[2,0],[1,1],[3,0],[2,1],[4 排序,0],[3,1],[4,1] switchMap - 最后一个 [3,0],[4,0],[4,1] 节流

主要的收获是 switchMap 的输出是不可预测的,这取决于内部 observable 的时间,例如,如果内部是 http get 结果可能取决于连接速度。

console.clear() const { mergeMap,flatMap,concatMap,switchMap,exhaustMap,延迟,map,take,toArray } = Rx.operators; const note = { mergeMap: '按内部 observable 排序', concatMap: '按外部 observable 排序', switchMap: 'Throttle by last', exhaustMap: 'Throttle by first', } const title = (operator) => { const opName = operator.name.replace('$1','') return `${opName} - ${note[opName]}` } const display = (x) => { return map(y => `[${x },${y}]`) } const inner = (x) => Rx.Observable.timer(0,500) .pipe( delay((5-x)*200), display(x), take(2) ) const example = operator => () => { Rx.Observable.interval(500).take(5) .pipe( operator(x => inner(x)), toArray(), map(vals => vals.join (',')) ) .subscribe(x => { console.log(title(operator)) console.log(x) }); }; const run = (fn1, fn2) => { console.clear() fn1() fn2() } const mmVcm = () => run(example(mergeMap), example(concatMap)); const smVem = () => run(example(switchMap), example(exhaustMap)); const mmVsm = () => run(example(mergeMap), example(switchMap)); .examples > div { 光标:指针;背景颜色:#4CAF50;白颜色;填充:7px 16px;显示:内联块; }

mergeMap vs concatMap
switchMap vs exhaustMap
mergeMap vs switchMap


我想在“mergeMap vs switchMap”部分指出,mergeMap也是不可预测的,因为不同的请求可能需要不同的完成时间,这会改变最终发射的顺序。假设您只想显示最后一个发出的值,但它必须是 最后一个给定输入(外部发出) 的最后一个值,那么这两个都不适合您,您不得不改用 concatMap . (不过,这是理论上的,我想不出这个要求的真实示例。无论如何,要点是 both mergeMapswitchMap 都可能是不可预测的。)
Y
Yilmaz

假设您订阅了一个天气频道。天气播音员在运行了几次操作后阅读了传递给他的报告。

如果播音员正在阅读一份报告,而在阅读另一份报告时进来了。如果他停止阅读第一份报告并在新报告到达后立即开始阅读,那么他正在执行 switchMap。因为 switchMap 将每个源值投射到一个 observable 中,该 observable 被合并到输出 observable 中,只从“最近”投射的 observable 中发射值。

如果广播播音员直到第一个报告结束才开始新的报告,我们有 concatMap。 concatMap 将每个源值投影到一个可观察对象,该可观察对象以序列化方式合并到输出可观察对象中,等待每个源值完成,然后再合并下一个。

如果在播音员仍在阅读时收到新报告,并且他的回应是同时阅读两者,那么我们就有了 mergeMap/flatMap。 ( flatMap 是 mergeMap 的别名)。因为 mergeMap 将每个源值投影到一个 observable,该 observable 被合并到输出 observable 中。 “mergeMap”是switchMap和concatMap的一个更基础的版本。


很好的比喻。感谢你的回答
尽管您已在 7 年前回答了这个问题,但我发现一口气读完它非常有用。万分感谢!!
@AjeetSingh 我在 2021 年 9 月 14 日回答了这个问题,而不是在 2014 年 :)
:-D,对不起,9 月 14 日,我读的是 2014 年 9 月。但是您的回答为我节省了很多时间。
j
jadc

这是思考不同类型地图之间差异的另一种方式。这帮助我解决了这个问题。我希望它可以帮助其他人。

考虑以下来源:

从字母表中产生小写字母的来源:a,b,c & d

个独立的“单词”来源,每个产生 3 个单词,以字母表中的一个特定字母开头 - a、b、c 或 d - 然后完成

为了说明不同类型的地图之间的差异,我们将把来自字母表源的项目链接到与该字母表对应的“单词”源,使用每个不同的地图来查看不同的结果。

Map

这与其他地图不同,因为它没有引入另一个可观察的来源。它只是将传入的值转换为另一个值。

因此,小写源的输出,通过将输入转换为大写的 Map,将是:

Input: a,b,c,d

Output: A, B, C, D

Switch Map

这会将每个输入转换为另一个源,将输出切换为来自该新源(即订阅该新源)。当另一个 alpha 输入到达时,“word”源会发生变化(我们取消订阅之前的“word”源)。

Input: a,b,c,d

Output: animal, aardvark, bull, baker, beach, cow, dog, day, dinner

Concat Map

与 switchMap 类似,只是 Concat 等到每个源完成后再继续下一个。

Input: a,b,c,d

Output: animal, aardvark, axe, bull, baker, beach, cow, car, cat, dog, day, dinner

Exhaust Map

与 Concat Map 类似,只是它会在完成最后一个源时忽略任何输入。下面的示例假设 alpha 输入“b”和“d”都在前一个映射源仍在完成时进入,因此它们被忽略。

Input: a,b,c,d

Output: animal, aardvark, axe, cow, car, cat

Merge Map(又名平面地图)

就像 concatMap 一样,每个源都运行到完成,但是一个新的源可以在其他源仍在运行时启动 - 所以序列重叠。

Input: a,b,c,d

Output: animal, aardvark, bull, axe, baker, cow, car, beach, dog, day, cat, dinner

P
Picci

至少对我来说,一开始要掌握这一点有点长。

无论如何,考虑一下:

flatMapmergeMap 的另一个名称 - mergeMap 方法接受一个可选参数 concurrency,它定义了可以同时订阅多少个 Observable

concatMap 等于 mergeMap,并发设置为 1

使用 mergeMap,您不会丢失您正在合并的 Observables 发出的任何事件,正如您在答案中所建议的那样

switchMap 如您所描述的那样工作(有关详细信息,请参阅这篇精彩的文章 https://blog.angular-university.io/rxjs-switchmap-operator/


k
knoefel

不久前,我为使用请求的运算符做了一个小演示/示例。

https://stackblitz.com/edit/rxjs-map-operators

您可以选择间隔或单击以发出外部可观察值。对于内部 observable,您可以选择是否发出间隔(3 个项目)或 http 请求。

它将在选择下方打印结果。