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]消息将被注销。一段时间后,解决了以前的长时间内部订阅,并返回记录的消息
我尝试使用switchMap和mergeMap操作符,但没有成功
[编辑]:我需要的功能是作为块执行,下一次外部执行需要在第一次执行完成后执行(外部和内部订阅)。使用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以避免