takeWhile、takeUntil、采取什么?
您可能已经知道,有时您需要取消订阅Observables。
取消订阅的方法有很多种。在几个项目中,我发现同时使用了 `--unsubscribe` 和takeWhile`--unsubscribe` takeUntil。这就引出了一个问题:为什么会有两个听起来相似、功能也类似的函数呢?
关于 takeWhile
好,我们来看一个简单的例子。我们看到的第一个代码片段是用来takeWhile取消订阅的Observable。
在这个例子中,我使用了两个不同的 Observable 。第一个 Observable 使用`interval` 操作符Observables创建。它会一直发出通知,直到传递给它的条件为假。在 `interval` 内部,我们使用一个布尔变量来描述用户是否已经点击过。一旦用户点击了屏幕上的某个位置,我们就取消订阅该 Observable。为了确定用户是否已经点击过,我们使用了第二个 Observable ,它使用`fromEvent` 操作符创建。此外,我们还使用了`tap` 操作符,将通知记录到控制台中。我们可以看到,一旦没有新的日志传入,我们的 Observable 就会被取消订阅。takeWhiletakeWhileintervalObservable
关于 takeUntil
从宏观角度来看,这些代码片段看起来差别不大。Observable我们不再使用布尔属性来描述状态,而是直接使用了clickObservable 对象。
我们将这个ObservableObservable 实例传递给takeUntil操作符,一旦用户点击某个位置,intervalObservable 对象就会被取消订阅。
问题
所以总的来说,这两个代码片段看起来相似,行为也相似,对吗?其实不然!
让我们来看看描述这些运算符的弹珠图,因为这将突出这两个运算符之间的区别。

takeUntil 大理石图,由Michael Hladky友情提供

大理石纹理图,由Michael Hladky友情提供
这里的问题在于,` takeWhileonNotification` 的目的是接收传入的通知并检查其是否满足特定条件,这可能会导致取消订阅。关键在于,` takeWhileonNotification` 是由传入的通知触发的,之后可能会取消订阅。而 `onNotification`takeUntil是由传递的 `onNotification` 触发的Observable。
这就是为什么 ` takeWhileonNotification` 可能会导致一些问题。因此,取消订阅肯定需要一个新的通知。想象一下,如果你有一个长期存在的 `onNotification`,那么使用 `onNotification`Observable需要的通知takeWhile比使用 ` onNotification` 多一个takeUntil。此外,这个额外的通知可能会在你的 `onNotification` 中启动多个进程Observable。想象一下,你的代码可能是这样的:
longLivingObservable$
.pipe(
tap(() => this.startAnimation()),
switchMap(val => this.makeHttpCall(val)),
takeWhile(val => this.alive),
)
.subscribe();
那么这段代码有什么问题呢?我们的组件已经被销毁了,而由于在取消订阅生效之前需要发送通知,我们会启动一个动画并触发一个 HTTP 请求。这可能是我们不希望看到的,而且之后我们还会检查是否要取消订阅Observable。除了这些操作完全多余之外,它们还可能导致应用程序崩溃或污染状态。
此外,如果我们的组件Observable不发出额外的值,则takeWhile永远不会触发该事件,因此我们的Observable组件永远不会被取消订阅。这可以被视为内存泄漏,因为我们的Observable组件会一直保持订阅状态。
现在或许有人会说:“好吧,我可以将takeWhile运算符移到可观测管道的最开始!”
没错,你可以这样做,这样可以避免不必要的操作,这固然是个好的开始,但你不会取消订阅内部的 Observable。所以,如果 ` Observablereturns` 返回的makeHttpCall是一个长期存在的Observable,那么即使 `unsubscribe` 操作符在管道中位于 `returns`之前Observable,它也不会取消订阅该 Observable 。顺便一提,`unsubscribe` 操作符也存在同样的问题,所以请确保将取消订阅操作符放在管道的最后。takeWhileswitchMaptakeUntil
解决方案
别误会,`unsubscribe`takeWhile是个很棒的操作符,但前提是你真的要用传入的值来判断是否要取消订阅!使用 `unsubscribe` 时不要依赖“全局”状态takeWhile。
对于这种情况,请坚持takeUntil使用 `Subject` 实例来触发它。
长轮询机制就是一个实际应用场景takeWhile。想象一下,你需要获取一个描述某个进程的资源。这个进程可能已经成功完成,也可能仍在进行中。当然,你希望在进程尚未完成时持续轮询。这种情况下的代码可能如下所示。
longPolling$.pipe(takeWhile(process => process.completed)).subscribe(() => handleNotCompleted());
在这种情况下,使用收到的遗嘱来决定是否继续订阅takeWhile是理想的选择!如果有外部触发条件,则继续使用takeUntil。
包起来
- 当传入的值导致您想要取消订阅时,请使用 takeWhile
- 当存在外部事件时,使用 takeUntil 来确定是否要取消订阅。
- 将它们都用作
Observable管道 中的最后一个运算符。
特别鸣谢
我衷心感谢所有帮助我撰写这些博客文章的了不起的人们。
谨以此文献给: