首页 > 解决方案 > 如何使用 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)
    )
  ),
);

标签: angularrxjsangular-materialangular2-observables

解决方案


mergeMap而不是嵌套subscribe

mergeMap 完成嵌套订阅所做的所有事情,但它还允许您在发出订阅值时继续您的逻辑。


快速抛开:

如果您订阅的 observable 发出一次并完成(如 http 请求),switchMapmergeMap产生相同的输出。在这些情况下,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_concatforEach(stream.subscribe())

您会在上面的代码中注意到,当需要重新编写您的 forEach 循环时,我使用了 and 的组合mergeArray#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);

推荐阅读