Rxjs 为什么使用共享会阻止可观察的使用withLatestFrom?

Rxjs 为什么使用共享会阻止可观察的使用withLatestFrom?,rxjs,Rxjs,问题 我有以下操作员: const prepare = (value$: Observable<string>) => value$.pipe( tap((x) => console.log("prepare: ", x)), share() ); const performTaskA = (removed$: Observable<string>) => removed$.p

问题

我有以下操作员:

  const prepare = (value$: Observable<string>) =>
    value$.pipe(
      tap((x) => console.log("prepare: ", x)),
      share()
    );

  const performTaskA = (removed$: Observable<string>) =>
    removed$.pipe(tap((x) => console.log("taskA: ", x)));

  const performTaskB = (removed$: Observable<string>) =>
    removed$.pipe(
      tap((x) => console.log("taskB 1: ", x)),
      withLatestFrom(otherValue$),
      tap((x) => console.log("taskB 2: ", x))
    );
产生以下输出:

prepare:  TEST 
taskA:  TEST 
taskB 1:  TEST
请注意,
taskb2
未被记录-在
performTaskB
中,
taskBDone
可观察到的
已在
与最新自(其他值$)
处暂停

如果
prepare
中的
share
被删除,则可观察对象不会暂停,但它(毫不奇怪)会导致
prepare
执行两次,这是我不希望看到的

问题

  • 如何执行
    performTaskA
    performTaskB
    prepare
    只能执行一次
  • 根据下面的调试说明,
    share
    为什么会导致发射序列的更改
  • 演示
    有股份(如上所述):
    无股份:

    转到右侧的测试选项卡,确保控制台可见,然后单击播放按钮

    部分解释
    使用LatestFrom调试
    ,很明显,当源(
    已删除$
    )发射时,
    就绪
    ,这会阻止发射

    出现这种情况的原因是,当存在
    share
    时,输入(
    otherValue$
    )订阅在源之后发出,因此尚未设置
    ready
    。(或者是
    share
    导致源更早发射的原因?)


    但是当
    share
    被删除时,输入订阅会在源代码之前发出,这意味着
    ready
    通过and设置为true,因此
    withLatestFrom
    会按预期发出。

    我尝试运行您的代码,但无法像您的代码那样将其挂起。我怀疑这与您的测试框架如何管理可观察性有关。我无法复制它

    我确实注意到,您所写的内容中存在一些固有的交错/排序问题。共享同步观测值会带来一些问题,例如第一个观测者同步观测所有源值,并在第二个观测者订阅之前完成观测。即使它们应该“同时”订阅,也会发生这种情况

    使用事件循环进行排序 我在这里写的东西对我很有用:

    函数初始化(
    价值$:可观察到,
    otherValue$:可观察
    ) {
    常量准备=管道(
    轻触(x=>console.log(“准备:,x)),
    延迟(0),
    股份()
    );
    const performTaskA=管道(
    轻触(x=>console.log(“taskA:,x))
    );
    const performTaskB=管道(
    点击(x=>console.log(“taskb1:,x)),
    withLatestFrom(otherValue$),
    点击(x=>console.log(“taskb2:,x))
    );
    const prepared$=值$.pipe(prepare);
    合并(
    准备好的美元管道(performTaskA),
    已准备的美元管道(performTaskB)
    ).subscribe();
    }
    初始(属于(“a”)、属于(“b”);
    
    对我来说,这会将其打印到控制台:

    prepare:a
    taskA:a
    任务B 1:a
    任务b 2:[“a”、“b”]
    
    您将注意到在
    prepare
    中调用delay(0)。如果不这样做,则会调用prepare两次,因为共享的可观察对象已同步完成。延迟(0)只是尽快将下一个调用放入事件队列

    这不是最好的解决办法。这是一个黑客。最佳解决方案取决于如何使用。大多数情况下,shareReplay(1)完成了这项工作。如果你在这里用,那就行了

    使用发布/连接进行订购 否则,您可以发布并连接,以确保订单符合预期

    发布/连接允许您在源正式启动/连接/订阅之前设置对源的所有订阅。这确保了在任何事情发生之前,所有回调和恢复都已就绪。这是确保完全同步的可观察对象可以共享其值的唯一方法

    函数初始化(
    价值$:可观察到,
    otherValue$:可观察
    ) {
    常量准备=管道(
    轻触(x=>console.log(“准备:,x)),
    股份()
    );
    const performTaskA=管道(
    轻触(x=>console.log(“taskA:,x))
    );
    const performTaskB=管道(
    点击(x=>console.log(“taskb1:,x)),
    withLatestFrom(otherValue$),
    点击(x=>console.log(“taskb2:,x))
    );
    const prepared$=publish()(值$.pipe(prepare));
    合并(
    准备好的美元管道(performTaskA),
    已准备的美元管道(performTaskB)
    ).subscribe();
    已准备好$.connect();
    }
    
    performTaskB
    中,您直接在
    中使用
    值$
    ,而不是
    prepare
    返回的共享版本。但是
    removed$
    参数实际上是共享的
    value$
    ,因此我们在这个函数中使用了两个不同版本的
    value$
    。这是故意的吗?这是故意的,因为它显示了我的问题。不过,我已经编辑了这个问题,因此现在
    withLatestFrom
    引用了一个单独的变量,该变量更接近我的真实示例,并且仍然显示了这个问题。谢谢-我认为解释可能与您今天的另一个问题相似,但不完全相同。在本例中,您传递的“测试”可观测值(以及
    otherValue$
    )是什么?我通过
    TestScheduler
    /marbles将它们作为冷可观测值传递-请参见-这有点像我的真实世界应用程序,
    value$
    主题
    ,并且
    $otherValue
    连接到ngrx选择器。不管怎样,我在这里和我的应用程序中都有同样的问题。谢谢。@backtick我已经深入研究了这个问题,并相应地更新了这个问题。非常感谢您的回答,并对延迟回复表示歉意。我很欣赏这个解释,但是第一个解决方案并不代表我的场景(其中输入观察值不完整),第二个解决方案仍然显示出与我最初遇到的问题相同的问题-请参见-从右侧的测试选项卡运行测试,并观察右下角的控制台输出。我说我有
    prepare:  TEST 
    taskA:  TEST 
    taskB 1:  TEST