为什么在RxJS中使用finalize?

为什么在RxJS中使用finalize?,rxjs,Rxjs,我无法理解RxJS中的运算符。让我用一个例子来说明这一点: of(null).pipe( tap({ complete: () => console.log('tap 1 completes') }), finalize(() => console.log('finalize')), tap({ complete: () => console.log('tap 2 completes') }) ).subscribe({ complete: () => con

我无法理解RxJS中的运算符。让我用一个例子来说明这一点:

of(null).pipe(
  tap({ complete: () => console.log('tap 1 completes') }),
  finalize(() => console.log('finalize')),
  tap({ complete: () => console.log('tap 2 completes') })
).subscribe({ complete: () => console.log('subscribe completes') });
我希望在第二次点击
之前执行
finalize
回调。但这并没有发生。相反,上述代码生成以下输出:

tap 1 completes
tap 2 completes
subscribe completes
finalize
从这个角度来看,我相信算符是通过(提升)整个可观察链的,始终应用在它的末端。现在我有两个问题:

  • 这个设计决策背后的基本原理是什么?你能解释一下为什么这是一个理想的/有利的财产吗
  • 是否有不同的操作员或其他解决方案在完成和出错时按顺序(即在上例中的第二次
    点击之前)而不是在可观察链的末端执行代码

  • 这就是
    finalize
    操作符的全部原理。仅在源可观测完成后发射。这意味着,在处理完所有完整的订阅后,计算
    tap
    complete。从文档中:

    返回一个Observable,该Observable镜像源Observable,但当源在完成或出错时终止时将调用指定函数

    现在您可以将
    finalize
    放在一个内部可观察对象中,但我想您也不会喜欢这样的顺序

    of(null).pipe(
      tap({ complete: () => console.log('tap 1 completes') }),
      concatMap((resp) => of(resp).pipe(
        finalize(() => console.log('finalize'))
      )),
      tap({ complete: () => console.log('tap 2 completes') })
    ).subscribe({ complete: () => console.log('subscribe completes') });
    
    这将使
    finalize
    在第一次和最后一次点击之前执行,这是因为传递到
    tap
    complete
    对象。如果您只需在第一个
    点击中传入一个函数,它将具有正确的顺序

    另一种方法是使用
    concat

    concat(
      of(null).pipe(
        tap({complete: () => console.log('tap 1 completes' ) }),
        finalize(() => console.log('finalize'))
      ),
      EMPTY.pipe(
        tap({complete: () => console.log('tap 2 completes' ) })
      )
    ).subscribe({ complete: () => console.log('subscribe completes') });
    

    但这会阻止你访问第一个可观测到的发射物。因此,基本上,我认为没有一个合适的解决方案来解决您的问题:)

    重要的是要知道,
    finalize()
    tap()
    的工作方式非常不同
    tap()
    next
    error
    complete
    通知触发,而
    finalize()
    仅在链订阅时触发。换句话说,
    finalize()
    非常类似于使用:

    const subscription = $source.subscribe();
    // This will be always triggered after all `tap()`s
    subscription.add(() => console.log('same as finalize()'));
    
    因此,您不能在点击()之前调用
    finalize()
    。另外,请注意,手动取消订阅liek时也会调用
    finalize()

    subscription.unsubscribe(); // will always invoke `finalize()` but never `tap()`
    
    一种可能的解决方案是实现您自己的
    finalize()
    变量,该变量知道调用它的原因:


    另外请注意,这对您的用例没有影响。

    有趣!到目前为止,我认为
    finalize
    相当于
    Promise.finally
    。显然,这里还有更多的事情要做。谢谢你的建议!现在我想我实际上是在搜索
    tap
    的变体,而不是
    finalize
    的变体。我想我可以自己定义:
    const-tapOnEnd=callback=>tap({complete:()=>callback(),error:()=>callback()})
    @kremerd我有点确定
    finalize
    也会在那之后被调用。你是对的。我的意思是用它代替
    finalize
    操作符,而不是代替另一个
    tap
    s。这样,执行顺序应该是直接的,因为它只是处理一系列的
    tap
    s。我忘记了,当出现可观察到的错误时,它也会调用
    complete
    。如果希望这些函数相同,只需使用
    complete
    。检查(我已经实现了您的
    tapOnEnd
    ),我认为在出现错误时不会调用
    complete
    。我也在a中尝试过,只调用了
    error
    回调。也许我错过了什么。“此处”链接已断开,因此我无法检查您的实现。