有人,请用Javascript解释SwitchMap和FlatMap之间的区别(从角度来看,rxjs 5)
在我的理解中。
SwitchMap 只发出最新的 observable 值并取消之前的 observable。
flatMap 收集所有单独的 observables 并在一个数组中返回所有 observables,而不关心 observable 的顺序。异步工作。
concatMap 保留顺序并发出所有可观察值,同步工作
那正确吗?
mergeMap 与上面的工作方式有何不同?
有人,请举例说明。
取自 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;显示:内联块; }
在下面的弹珠图中,以 5ms、10ms、20ms 发出的源流将 *映射 到timer(0, 3)
,限制为 3 次排放:
https://i.stack.imgur.com/zNfwS.png
在这里玩这个大理石图:"mergeMap vs exhaustMap vs switchMap vs concatMap"
已经有了所有这些很棒的答案,我想添加一个更直观的解释
希望它可以帮助某人
@ZahiC,很酷的答案 - 我喜欢在代码示例中使用功能组合。如果可以的话,我想借用它来说明一些使用定时可观察的额外点。
外部、内部和控制
这些操作符都是 transformation operators 和 map()
一样,共同的特点是它们有一个 outer 和 inner 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
也是不可预测的,因为不同的请求可能需要不同的完成时间,这会改变最终发射的顺序。假设您只想显示最后一个发出的值,但它必须是 最后一个给定输入(外部发出) 的最后一个值,那么这两个都不适合您,您不得不改用 concatMap
. (不过,这是理论上的,我想不出这个要求的真实示例。无论如何,要点是 both mergeMap
和 switchMap
都可能是不可预测的。)
假设您订阅了一个天气频道。天气播音员在运行了几次操作后阅读了传递给他的报告。
如果播音员正在阅读一份报告,而在阅读另一份报告时进来了。如果他停止阅读第一份报告并在新报告到达后立即开始阅读,那么他正在执行 switchMap。因为 switchMap 将每个源值投射到一个 observable 中,该 observable 被合并到输出 observable 中,只从“最近”投射的 observable 中发射值。
如果广播播音员直到第一个报告结束才开始新的报告,我们有 concatMap。 concatMap 将每个源值投影到一个可观察对象,该可观察对象以序列化方式合并到输出可观察对象中,等待每个源值完成,然后再合并下一个。
如果在播音员仍在阅读时收到新报告,并且他的回应是同时阅读两者,那么我们就有了 mergeMap/flatMap。 ( flatMap 是 mergeMap 的别名)。因为 mergeMap 将每个源值投影到一个 observable,该 observable 被合并到输出 observable 中。 “mergeMap”是switchMap和concatMap的一个更基础的版本。
这是思考不同类型地图之间差异的另一种方式。这帮助我解决了这个问题。我希望它可以帮助其他人。
考虑以下来源:
从字母表中产生小写字母的来源:a,b,c & d
个独立的“单词”来源,每个产生 3 个单词,以字母表中的一个特定字母开头 - a、b、c 或 d - 然后完成
为了说明不同类型的地图之间的差异,我们将把来自字母表源的项目链接到与该字母表对应的“单词”源,使用每个不同的地图来查看不同的结果。
这与其他地图不同,因为它没有引入另一个可观察的来源。它只是将传入的值转换为另一个值。
因此,小写源的输出,通过将输入转换为大写的 Map,将是:
Input: a,b,c,d
Output: A, B, C, D
这会将每个输入转换为另一个源,将输出切换为来自该新源(即订阅该新源)。当另一个 alpha 输入到达时,“word”源会发生变化(我们取消订阅之前的“word”源)。
Input: a,b,c,d
Output: animal, aardvark, bull, baker, beach, cow, dog, day, dinner
与 switchMap 类似,只是 Concat 等到每个源完成后再继续下一个。
Input: a,b,c,d
Output: animal, aardvark, axe, bull, baker, beach, cow, car, cat, dog, day, dinner
与 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
至少对我来说,一开始要掌握这一点有点长。
无论如何,考虑一下:
flatMap
是 mergeMap
的另一个名称 - mergeMap
方法接受一个可选参数 concurrency
,它定义了可以同时订阅多少个 Observable
concatMap
等于 mergeMap
,并发设置为 1
使用 mergeMap
,您不会丢失您正在合并的 Observables 发出的任何事件,正如您在答案中所建议的那样
switchMap
如您所描述的那样工作(有关详细信息,请参阅这篇精彩的文章 https://blog.angular-university.io/rxjs-switchmap-operator/)
不久前,我为使用请求的运算符做了一个小演示/示例。
https://stackblitz.com/edit/rxjs-map-operators
您可以选择间隔或单击以发出外部可观察值。对于内部 observable,您可以选择是否发出间隔(3 个项目)或 http 请求。
它将在选择下方打印结果。
flatMap
是mergeMap
的别名,这就是它打印出mergeMap
的原因。flatMap
将在 v8 中从 RxJS 中删除。