【问题标题】:nested subscription, inner subscription is dependent on outer subscription嵌套订阅,内订阅依赖外订阅
【发布时间】:2020-10-17 08:59:07
【问题描述】:

我在 Angular 组件中有一个嵌套订阅。我无法与您分享代码,但我会尝试解释我面临的情况。情况是这样的

ParentComponentA

result:any;
mystatus: boolean = false;
@ViewChild(ChildComponentA): childComponentA;

this.myService.getResult().subscribe(res =>{
    
   this.result = res;

   // in the first subscribe there will be a lot of calculations based on res
   /*the second service  will take one of the 
    values as an argument which will be there in res, let's say a flag*/

   this.mySecondService.getStatus(this.result.flag).subscribe(flagSatus => {

    this.myStatus = flagStatus; //flagStatus will return true value

  /*now here I have a function named myFunction, which will use this.results
    values and flagStatus to do some calculation*/

    myFunction(this.res.profile, this.myStatus)
             });
      });

  myFunction(profile, status) {
  /* here childComponentA is coming undefined 
     when I'm printing in it console therefore it is not going in if condition*/
  if(this.childComponentA) {
   }
 }


HTML template A
<div *ngIf="myStatus">
  <! -- child-comp is selector of ChildComponentA -->
   <child-comp> </child-comp>
</div>

现在,一切正常,但是我从内部订阅中获得的 flagStatus,我在 html 模板中使用它。 childComponentA 正在显示,但是当我执行 console.log(this.childComponentA) 时,它是未定义的。这就是我的确切问题,这可能是因为我最初的 myStatus 值为 false。但是当我将整个内部订阅放入 setTimeOut 时,一切正常,如果 myFunction 的条件也正常。

为了避免使用 setTimeOut,我想使用 rxjs 以高效的方式实现我想要做的事情。我到处查找,但找不到与我相似的示例。根据我的发现,我们可以使用 switchMap 来做我想做的事。我的问题是我该如何使用它或者我可以使用任何其他运算符?请帮我解决这个问题。

【问题讨论】:

  • 作为一般经验法则,您永远不应该在 rxjs 中的另一个订阅中包含订阅。相反,您应该使用展平运算符从一个可观察对象跳转到另一个对象。四个展平操作符是 switchMap、mergeMap、concatMap 和 exhaustMap。通常 switchMap 或 mergeMap 会完成这项工作。学习如何使用它们可能很难,但绝对值得。如果你谷歌的话,有几篇关于 rxjs 扁平化操作符/策略的文章。
  • 我最近讨论了这个话题,可能会有所帮助。你可以在这里找到幻灯片:docs.google.com/presentation/d/…(看起来他们还没有发布谈话的录音。)

标签: angular typescript asynchronous rxjs observable


【解决方案1】:

您可以使用 mergeMap 订阅多个 Observable。

const result1$ = this.myService.getResult();
result1$.pipe(
  mergeMap(res => this.mySecondService.getStatus(res.flag).pipe(flagSatus => {
    this.myFunction(res, status);
  })));

【讨论】:

  • 感谢您的帮助,但请您详细说明您的答案。我会将第一次订阅中发生的所有计算放在哪里?
  • 代替res =&gt; this.mySecondService.getStatus(...)...,您可以像这样使回调函数多行:res =&gt; { // your calculations here; return this.mySecondService.getStatus(...)...}
【解决方案2】:

你可以使用 switchMap 因为你试图从一个 observable 中获取一个基于另一个 observable 的结果的结果。 您可以在此处阅读有关 switchMap 的更多信息。 https://www.learnrxjs.io/learn-rxjs/operators/transformation/switchmap

【讨论】:

    【解决方案3】:

    我已经使用 Rxjs 主题解决了这个问题,它在可观察对象完成时充当事件发射器。如果保存最后一个值是有意义的(例如,如果您有一个将在其他服务中使用的 id),您可以使用 behaviorSubject。 What is the difference between Subject and BehaviorSubject?

    import { BehaviorSubject } from 'rxjs/BehaviorSubject';
    
     mySubject = new BehaviorSubject(null);
    
    // This would be the first request that loads data
    getData() {
    return this.http.get<any>(Services, options).map(
      res => {
        console.log('res from service', res);
        this.mySubject.next(res});        
      });
    }
    

    现在,如果您有另一个组件需要此数据并且之前已加载,它仍然可用

    this.service.mySubject.subscribe( (res:any) => {
        this.http.get<any>(res.someParam , options).map(
        ....
      });
    }
    

    【讨论】: