Rxjs嵌套订阅有多个内部订阅

时间:2019-10-11 11:05:56

标签: javascript design-patterns rxjs

我要重写的基于原始承诺的代码:

parentPromise
    .then((parentResult) => {
        childPromise1
            .then(child1Result => child1Handler(parentResult, child1Result));
        childPromise2
            .then(child1Result => child2Handler(parentResult, child2Result));
        childPromise3
            .then(child1Result => child3Handler(parentResult, child3Result));
    });   

在以下情况下,我试图找到一种避免nested subscriptions anti-pattern的方法:

parent$
    .pipe(takeUntil(onDestroy$))
    .subscribe((parentResult) => {
        child1$
            .pipe(takeUntil(onDestroy$))
            .subscribe(child1Result => child1Handler(parentResult, child1Result));
        child2$
            .pipe(takeUntil(onDestroy$))
            .subscribe(child2Result => child2Handler(parentResult, child2Result));
        child3$
            .pipe(takeUntil(onDestroy$))
            .subscribe(child3Result => child3Handler(parentResult, child3Result));
    });

执行此操作的正确“ RxJS方法”是什么?

4 个答案:

答案 0 :(得分:1)

这对我来说似乎很奇怪。您每次parentResult到来时都会为每个孩子创建新的订阅。即使这些最终确实会被销毁(假设实现onDestroy$是正确的),这似乎也是错误的。

您可能希望为每个孩子使用withLatestFrom(parent$)和三个单独的管道。

它可能看起来像: child1$.pipe(takeUntil(globalDeath$), withLatestFrom(parent$)).subscribe(([childResult, parentResult]) => ...)。不确定我的JS是否正确,目前无​​法对其进行测试;但关键是:每次parent$触发时,您都会从child1$获得最新的结果。请注意,如有必要,可以反转方向(withLatestFrom(child1$))。

答案 1 :(得分:0)

您可以:1)通过parent$传递share,以及2)使用flatMap三次,类似:

const sharedParent$ = parent$.pipe(share());

sharedParent$.pipe(
    flatMap(parentResult => forkJoin(of(parentResult), child1$)), 
    takeUntil(onDestroy$)),
.subscribe((results) => child1Handler(...results)); // repeat for all children

(如果有两个以上的子代,则以子流和处理程序作为参数将其提取到函数中是个好主意)。

这是遵循与订阅孩子等待直到parent$发出的原始行为。如果不需要,可以跳过flatMap,而只跳过forkJoin sharedParent$和子级。

答案 2 :(得分:0)

使用高阶可观察物怎么样?像这样:

const parentReplay$ = parent$.pipe(shareReplay(1));

of(
  [child1$, child1Handler],
  [child2$, child2Handler],
  [child3$, child3Handler]
).pipe(
  mergeMap([child$, handler] => parentReplay$.pipe(
    mergeMap(parentResult => child$.pipe(
      tap(childResult => handler(parentResult, childResult))
    )
  )
).subscribe();

答案 3 :(得分:0)

如果您使用的是Promises,则相应的Observable仅发出一次,然后完成。

在这种情况下,您可以使用forkJoin来并行执行子Observable。

所以代码看起来像

parent$.pipe(
   takeUntil(onDestroy$),
   // wait for parent$ to emit and then move on
   // the following forkJoin executes the child observables in parallel and emit when all children complete - the value emitted is an array with the 3 notifications coming from the child observables
   concatMap(parentResult => forkJoin(child1$, child2$, child3$)).pipe(
      // map returns both the parent and the children notificiations
      map(childrenResults => ({parentResult, childrenResults})
   )
).subscribe(
  ({parentResult, childrenResults}) => {
      child1Handler(parentResult, childrenResults[0]);
      child1Handler(parentResult, childrenResults[1]);
      child1Handler(parentResult, childrenResults[2]);
  }
)