angular - 如何使用 switchMap 而不是嵌套订阅?
问题描述
我还在学习 observables,所以如果有一个简单的解决方案,我不会感到惊讶。基本上我现在拥有的是四个嵌套订阅,第二个 subscribe() 中有一个fourEach。我看到了很多使用 switchMap 的答案,但我找不到一个也有一个 for 循环可以迭代的答案。我知道我可能应该使用嵌套订阅,但我不知道如何使用 forEach。
这是嵌套订阅的工作代码:
dialogRef.afterClosed().subscribe(result => {
if(result) {
this.createLikertResponseGroup(result.likertResponseGroup)
.subscribe(likertResponseGroupJSON => {
result.likertResponse.controls.likertResponseFormArray.controls.forEach((element) => {
let characteristic = element.controls.characteristic;
this.newResponseGroupId = likertResponseGroupJSON.LikertResponseGroup.id;
this.createLikertResponse(element, this.newResponseGroupId)
.subscribe(likertResponseJSON => {
if (characteristic) {
let responseId = likertResponseJSON.LikertResponse.id;
this.createCharacteristic(characteristic, this.newResponseGroupId, responseId)
.subscribe(characteristicJSON => {
this.newCharacteristicId = characteristicJSON.Characteristic.id;
});
}
});
});
})
}
});
我现在的作品。所以我的问题是,是否值得改变我这样做的方式?如果是这样,我会怎么做?
我还没有走多远,但是我对 switchMap 的尝试如下所示:
dialogRef.afterClosed().pipe(
filter(result => result != null),
switchMap(result =>
from(result.likertResponse.controls.likertResponseFormArray.controls).pipe(
// not sure what to do after this (or if I'm even doing it right)
)
),
);
解决方案
mergeMap
而不是嵌套subscribe
mergeMap 完成嵌套订阅所做的所有事情,但它还允许您在发出订阅值时继续您的逻辑。
快速抛开:
如果您订阅的 observable 发出一次并完成(如 http 请求),
switchMap
并mergeMap
产生相同的输出。在这些情况下,switchMap
通常建议使用。mergeMap
原因从调试内存泄漏到边际性能,再到其他开发人员的期望。为简单起见,我在这里忽略了这一点,并
mergeMap
在所有情况下都使用了它。
mergeMap
您可以通过嵌套和/或嵌套来隐藏一些复杂性,subscriptions
因为您可以依靠函数闭包在管道中更早地设置和记住值。
它也可能成为造成极大混乱的原因。众所周知,深度嵌套的函数很难在 JS 中调试,因此映射到中间对象以保存下一步所需的值(而不是通过函数闭包嵌套和获取中间值)的额外努力非常值得。
它也稍微快一些,因为运行时不需要在调用堆栈中向上移动以查找变量(但同样,您应该这样做,因为它更清洁、可维护和可扩展,而不是为了尽早优化)。
这是用 mergeMap 和包含中间值的对象完全重写的代码:
dialogRef.afterClosed().pipe(
filter(result => result), // <-- only "truthy" results pass same as if(result)
mergeMap(result =>
this.createLikertResponseGroup(result.likertResponseGroup).pipe(
map(likertResponseGroupJSON => ({result, likertResponseGroupJSON}))
)
),
mergeMap(({result, likertResponseGroupJSON}) => merge(
...result.likertResponse.controls.likertResponseFormArray.controls.map(
element => this.createLikertResponse(
element,
likertResponseGroupJSON.LikertResponseGroup.id
).pipe(
map(likertResponseJSON => ({
likertResponseJSON,
characteristic: element.controls.characteristic,
newResponseGroupId: likertResponseGroupJSON.LikertResponseGroup.id
}))
)
)
)),
filter(({characteristic}) => characteristic) // only "Truthy" characteristic allowed
mergeMap(({likertResponseJSON, characteristic, newResponseGroupId}) =>
this.createCharacteristic(
characteristic,
newResponseGroupId,
likertResponseJSON.LikertResponse.id
).pipe(
map(characteristicJSON => ({
newCharacteristicId: characteristicJSON.Characteristic.id,
newResponseGroupId
}))
)
)
).subscribe(({newCharacteristicId, newResponseGroupId}) => {
this.newResponseGroupId = newResponseGroupId;
this.newCharacteristicId = newCharacteristicId;
});
merge
//代替forkJoin
_concat
forEach(stream.subscribe())
您会在上面的代码中注意到,当需要重新编写您的 forEach 循环时,我使用了 and 的组合merge
而Array#map
不是Array#forEach
merge
是关闭等价于forEach(stream.subscribe())
,但其他人可以改变行为,甚至可以提高性能,或者只是让您直观地组合更复杂的流。
在这里,第 2 行和第 3 行具有相同的输出。然而,第二个很容易扩展更多的 RxJS 操作符
1. const arrayOfStreams = [s1,s2,s3,s4];
2. arrayOfStreams.forEach(s => s.subscribe(console.log));
3. merge(...arrayOfStreams).subscribe(console.log);
扩展:
arrayOfStreams.forEach(s => s.subscribe(value => {
if(this.isGoodValue(value)){
console.log(value.append(" end"))
}
}));
merge(...arrayOfStreams).pipe(
filter(this.isGoodValue),
map(value => value.append(" end"))
).subscribe(console.log);
推荐阅读
- c# - 实体框架 (MVC) - 为具有受保护属性的模型创建控制器时出现问题
- python - 如何编写带循环的函数
- mysql - 我需要为同一列使用 2 个索引还是一个就足够了?
- excel - excels“文件来源”的熊猫等价物
- css - css grid - 让网格向下而不是跨越五个相等的列
- php - 如何在 PHP 中安排定期更新 MySQL 数据库?
- javascript - 在 tensorflow JavaScript 中使用posenet时内存泄漏
- groovy - 在 groovy 中,如何返回零填充范围?
- python - 尽管在 views.py 中设置了 get_queryset 函数,但我的对象没有被过滤
- c++ - 如何制作程序谁会计算金字塔有多少阶段?