嵌套的可观察对象,其中第一个可观察对象的响应传递给第二个,并且两个响应都用于最终订阅

Nested observables where response from first observable is passed to the second and both responses are used in the final subscription

不使用嵌套订阅的正确写法是什么?

第一个 observable 将 return 传递给第二个 observable 的值,然后我需要在最终订阅中使用来自两个 observable 的 returned 值。

此外,“doStuff”方法是通过单击按钮调用的,我是否还需要取消订阅这些可观察对象以防止内存泄漏?

  doStuff() {

    // Get app user
    this.appUserService.getAppUser(true).subscribe((appUser) => {
      
      // Get login key
      this.accountApi.getLoginKey(appUser).subscribe(loginKey => {

        // Open in app browser
        this.browser = this.inAppBrowser.create(appUser.Url + loginKey, '_system');
      });
    });
  }

编辑 1

按照建议尝试了以下操作,但无法编译,在第二个管道上出现以下错误:

属性 'pipe' 在类型 'OperatorFunction<AppUser, any>'

上不存在

然后在“地图”功能中出现以下错误:

找不到名称 'appUser'。

   return this.appUserService.getAppUser(true).pipe(
      switchMap((appUser: AppUser) => this.accountApi.getUserLoginKey(appUser)).pipe(
        map(loginKey => { appUser, loginKey; }),
      ),
      tap(({ appUser, loginKey }) => {
        this.browser = this.inAppBrowser.create(appUser.Url + loginKey, '_system');
      }),
      take(1)
    );

编辑 2

我现在已经更新了我的解决方案,如下所示,它有效(但我可以争辩说我原来的解决方案确实有效),所以我删除了嵌套订阅,但它们已被嵌套管道取代。我相信这是一个更可接受的解决方案(即使用嵌套的“管道”而不是嵌套的“订阅”)但不确定是否可以进一步改进?

doStuff() {
       // Get app user
        this.appUserService.getAppUser(true).pipe(
          switchMap(appUser => this.accountApi.getLoginKey(appUser).pipe(
            tap(loginKey => {
              // Open in app browser as return URL
              this.browser = this.inAppBrowser.create(appUser.Url + loginKey, '_system');
            })
          )),
          take(1)
        ).subscribe();
}

没有“正确的方法”,你有的代码已经很好了。如果你想稍微改进一下,你可以使用 rxjs(例如 switchMap)。

关于退订部分,退订总是更好更安全。使用 Angular,您可以在 HTML 中使用异步管道,或者简单地在组件的销毁过程中取消订阅。

“正确”方式完全取决于您要查找的特定行为。如果您一次只希望一个请求处于活动状态,请使用 switchMap()exhaustMap()(新的点击将取消先前的请求或将分别被完全忽略)。这是一种方法:

doStuff() {
  return this.appUserService.getAppUser(true).pipe(
    switchMap(appUser => this.accountApi.getLoginKey(appUser).pipe(
      map(loginKey => { appUser, loginKey }),
    )),
    tap(({ appUser: /* appUser type */, loginKey: /* loginKey type */ }) => {
      this.browser = this.inAppBrowser.create(appUser.Url + loginKey, '_system');
    }),
    
    // OPTIONAL: add this take(1) to make sure Observable completes after first emission
    // if that is the desired behaviour (one way to prevent leaks)
    take(1)
  );
}

更新

修改了代码以删除已弃用的 switchMapresultSelector

第二次更新

更正括号。

这里有两个问题,

  1. 如何解决嵌套订阅情况? @Will Alexander 已经回答了这个问题。以下是一些可能对您有所帮助的参考资料
    https://angular-checklist.io/default/checklist/rxjs/Z1eFwa9
    https://www.thinktecture.com/en/angular/rxjs-antipattern-1-nested-subs/

  2. 如何优雅地取消订阅?
    a) 您可以手动取消订阅您的每个订阅(这很快就会变得混乱)
    b) 如果您不再需要 doStuff() 流,那么您可以按照说明使用 take(1)
    c) Angular 建议尽可能使用异步管道,以便框架执行订阅和取消订阅部分
    d) 使用 takeUntil 运算符取消订阅所有订阅(当您有许多订阅未使用异步管道管理时,这会派上用场)https://medium.com/@benlesh/rxjs-dont-unsubscribe-6753ed4fda87