简体   繁体   English

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

[英]Rxjs nested subscribe with multiple inner subscriptions

Original promise based code I'm trying to rewrite:我正在尝试重写基于原始 promise 的代码:

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

I'm trying to figure a way how to avoid the nested subscriptions anti-pattern in the following scenario:我试图想办法在以下情况下避免嵌套订阅反模式

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));
    });

What would be the correct 'RxJS way' to do this?这样做的正确“RxJS 方式”是什么?

That seems pretty strange to me.这对我来说似乎很奇怪。 You're creating new subscription for each child every time parentResult arrives.每次parentResult到达时,您都会为每个孩子创建新订阅。 Even though those eventually indeed will be destroyed (assuming onDestroy$ implementation is correct), seems wrong.尽管这些最终确实会被销毁(假设onDestroy$实现是正确的),但似乎是错误的。

You probably want withLatestFrom(parent$) and three separate pipes for each child.您可能需要withLatestFrom(parent$)和每个孩子的三个单独的管道。

It might look something like: child1$.pipe(takeUntil(globalDeath$), withLatestFrom(parent$)).subscribe(([childResult, parentResult]) =>...) .它可能看起来像: child1$.pipe(takeUntil(globalDeath$), withLatestFrom(parent$)).subscribe(([childResult, parentResult]) =>...) Not sure if my JS is correct, can't test it at the moment;不确定我的 JS 是否正确,暂时无法测试; but the point is: you're getting the latest result from the parent$ every time child1$ fires.但关键是:每次child1$触发时,您都会从parent$获得最新结果。 Note that you can reverse the direction if necessary ( withLatestFrom(child1$) ).请注意,如有必要,您可以反转方向( withLatestFrom(child1$) )。

You can: 1) pass parent$ through share , and 2) use flatMap three times, something like:您可以:1)通过share传递parent$ ,以及 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

(If there's more than 2 children, extracting that into a function with child stream and handler as parameters is a good idea). (如果有超过 2 个孩子,将其提取到 function 中,孩子 stream 和处理程序作为参数是一个好主意)。

That's following the original behavior of waiting with subscribing children until parent$ emits.这是遵循与订阅子等待直到parent$发出的原始行为。 If you don't need that, you can skip flatMap and just forkJoin sharedParent$ and children.如果你不需要,你可以跳过flatMap而只是forkJoin sharedParent$和 children。

How about using higher order observables?使用更高阶的 observables 怎么样? Something like this:像这样的东西:

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();

If you were using Promises then the corresponding Observables emit only once and then complete.如果你使用 Promises,那么对应的 Observables 只会发出一次然后完成。

If this is the case, you can use forkJoin to execute in parallel the child Observables.如果是这种情况,您可以使用forkJoin并行执行子 Observable。

So the code could look like所以代码看起来像

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]);
  }
)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM