Rxjs可观测,内部订阅耗时较长,外部可观测在后续请求中首先执行

Rxjs可观测,内部订阅耗时较长,外部可观测在后续请求中首先执行,rxjs,nested,observable,subscription,Rxjs,Nested,Observable,Subscription,我有一个按钮启动一个可观察的结果,然后我订阅结果,我有一个订阅,它依赖于以前的可观察结果 this.getData(params).subscribe(result => { // Make some first level process console.log('[Outter Execution]: ',result); this.getInnerData(result).subscribe(res => { // Make some

我有一个按钮启动一个可观察的结果,然后我订阅结果,我有一个订阅,它依赖于以前的可观察结果

this.getData(params).subscribe(result => {
    // Make some first level process
    console.log('[Outter Execution]: ',result);

    this.getInnerData(result).subscribe(res => {
        // Make some inner level process
        console.log('[Inner Execution]: ',res);
    });
});
根据用户的点击速度,顺序不同:

//User click slowly

[Outer Execution]
[Inner Execution]

[Outer Execution]
[Inner Execution]

//User start clicking quickly

[Outer Execution]
[Inner Execution]

[Outer Execution]
[Inner Execution]

[Outer Execution]
[Outer Execution]
[Outer Execution]

[Inner Execution]
[Inner Execution]
[Inner Execution]

[Outer Execution]
[Inner Execution]
[Outer Execution]
[Inner Execution]
如您所见,如果嵌套订阅需要很长时间,并且用户在解析内部订阅之前再次单击,则在解析内部执行之前,第一条[Outer Execution]消息将被注销。一段时间后,解决了以前的长时间内部订阅,并返回记录的消息

我尝试使用switchMapmergeMap操作符,但没有成功


[编辑]:我需要的功能是作为块执行,下一次外部执行需要在第一次执行完成后执行(外部和内部订阅)。

使用RxJS有很多方法,如果没有具体的示例,很难为您提供具体的答案。我能做的最好的事情就是从我的代码中提供一个示例,并希望它接近您想要实现的目标

我和他们的供应商有产品。当我得到一个产品时,它有一个供应商ID数组。因此,我需要获得产品,然后在该操作中获得供应商。(听起来与您的场景类似?)

这是我的密码:

  selectedProductSuppliers$ = this.selectedProduct$
    .pipe(
      filter(selectedProduct => Boolean(selectedProduct)),
      switchMap(selectedProduct =>
        forkJoin(selectedProduct.supplierIds.map(supplierId => this.http.get<Supplier>(`${this.suppliersUrl}/${supplierId}`)))
      ),
      tap(suppliers => console.log('product suppliers', JSON.stringify(suppliers)))
    );
selectedProductSuppliers$=this.selectedProduct$
.烟斗(
过滤器(selectedProduct=>Boolean(selectedProduct)),
开关映射(selectedProduct=>
forkJoin(selectedProduct.supplierIds.map(supplierId=>this.http.get(`${this.suppliersUrl}/${supplierId}'))
),
点击(suppliers=>console.log('productsuppliers',JSON.stringify(suppliers)))
);
此代码使用
开关映射
,以确保如果用户多次单击,它将切换到最新的选择

然后,它使用一个
forkJoin
来处理每个供应商ID,发出一个http get请求来检索每个供应商数据,并将它们加入到一个发出的数组中

返回值是可观察的

您可以在此处找到完整的示例:

更新:

如果将上述代码更改为使用
concatMap
而不是
switchMap
,它将处理每个请求,等待一个请求完成后再执行下一个请求


为了确保处理等待,您需要在主观察对象上使用
concatMap
,而不是在从属观察对象上使用。否则它不会等待设置

我在你最新的改变之前就开始了,但这是我想到的似乎有效的方法:

  private clickSubject = new Subject<number>();
  clickAction$ = this.clickSubject.asObservable();

  ngOnInit() {
    this.clickAction$
      .pipe(
        concatMap(value => this.mainProcess(value)
          .pipe(
            mergeMap(x => this.dependantProcess(x))
          )
        )
      )
      .subscribe();
  }

  onClick(value) {
    // Emits a value into the action stream
    this.clickSubject.next(value);
  }

  mainProcess(value) {
    console.log("[Emitted] Main", value);
    return of(value).pipe(delay(10));
  }

  dependantProcess(value) {
    console.log("[Emitted] Dependent", value);
    return of(value).pipe(delay(2000));
  }
private clickSubject=新主题();
clickAction$=this.clickSubject.asObservable();
恩戈尼尼特(){
单击此按钮。单击操作$
.烟斗(
concatMap(值=>this.mainProcess(值)
.烟斗(
mergeMap(x=>this.dependent进程(x))
)
)
)
.subscribe();
}
onClick(值){
//将值发射到动作流中
this.clickSubject.next(值);
}
主进程(值){
log(“[emissed]Main”,值);
返回(值)。管道(延迟(10));
}
依赖进程(值){
log(“[emissed]Dependent”,值);
返回(值)。管道(延迟(2000));
}
请注意,
concatMap
用于等待主进程

  • 此代码通过使用RxJS
    Subject
    定义动作流来响应用户的点击

  • 每次用户单击按钮时,动作流都会发出

  • 操作流管道使用
    concatMap
    缓存请求,并等待处理它,直到处理之前的请求

  • 当主进程发出时,从属进程执行

  • 管道完成后(主进程及其相关进程),下一个缓存请求将由
    concatMap
    处理

有道理吗

您可以在此处找到更新的代码:


问题和预期行为是什么?我需要同时执行(外部和内部),下一次外部执行需要在第一次完成执行(外部和内部订阅)后执行,您使用switchMap的方法是正确的。在订阅中包含订阅通常不是一个好主意,因为很难管理订阅并确保它们被正确取消订阅。我刚刚应用了此过程,但我需要完成前面的所有块(可以观察到主序列和从属序列,在您的示例中,主序列是selectedProduct$,从属序列是每个供应商ID的HTTP get)。顺序应该是,等待第一个序列(主序列和从属序列)完成,然后继续第二个clic过程(我不能取消前一个或延迟从属过程)。但是,当用户快速单击时,会执行main observable,之后,当依赖项完成时,会显示依赖项结果。因此,如果他们快速单击,您仍希望处理每次单击,而不仅仅是最后一次(最近一次)一个?如果是这样,那么您可以尝试
concatMap
而不是
switchMap
。我刚刚创建了一个所需用法的示例,其中包含一些数据来说明此过程(app.component.ts)如果您单击一个并等待,则该过程是正确的,但是如果您快速多次单击一个,则该过程不是连续的,尽管我使用了mergeMap或concatMap(在您更新它时)。您的依赖进程是否只发送一个项目?如果是,您不需要
forkJoin
。在我上面的示例中,每个产品选择都发出多个http请求以获取产品的供应商列表。此外,您的进程是同步的还是异步的?的
同步发送。依赖进程将发送一个请求我更新了stackblitz以避免