Keeping too many subscription objects around is a sign you’re managing your subscriptions imperatively, and not taking advantage of the power of Rx.
在代码中keep太多的subscription对象是不好的,意味着管理订阅太过生硬,且没有充分利用Rx。
For example,
class MyGenericComponent extends SomeFrameworkComponent {
updateData(data) {
// do something framework-specific to update your component here.
}
onMount() {
this.dataSub = this.getData().subscribe(data => this.updateData(data));
const cancelBtn = this.element.querySelector(‘.cancel-button’);
const rangeSelector = this.element.querySelector(‘.rangeSelector’);
this.cancelSub = Observable.fromEvent(cancelBtn, ‘click’)
.subscribe(() => {
this.dataSub.unsubscribe();
});
this.rangeSub = Observable.fromEvent(rangeSelector, ‘change’)
.map(e => e.target.value)
.subscribe((value) => {
if (+value > 500) {
this.dataSub.unsubscribe();
}
});
}
onUnmount() {
this.dataSub.unsubscribe();
this.cancelSub.unsubscribe();
this.rangeSub.unsubscribe();
}
}
The ugliness here is I’m imperatively managing unsubscriptions in multiple places in this fairly trivial example.
这代码的糟糕之处在于,在这个相当琐碎的示例中,在多个地方强制管理取消订阅。
The only real advantage to using this approach would be performance. Since you’re using fewer abstractions to get the job done, it’s likely to perform a little better. This is unlikely to have a noticeable effect in the majority of web applications however, and I don’t think it’s worth worrying about.
这种方法的惟一的优势是性能。因为使越少的抽象来完成工作,执行起来性能就会越好些。然而,这在大多数web应用程序中不太可能产生明显的影响。
Alternatively, you can always combine subscriptions into a single subscription by creating a parent subscription and adding all of the others like children. But at the end of the day, you’re still doing the same thing, and you’re probably missing out.Compose your subscription management with takeUntil:
另一种方法就是创建父订阅并添加所有其他订阅(作为子订阅),将订阅合并为单个订阅。使用takeUntil进行订阅的管理:
class MyGenericComponent extends SomeFrameworkComponent {
updateData(data) {
// do something framework-specific to update your component here.
}
onMount() {
const data$ = this.getData();
const cancelBtn = this.element.querySelector(‘.cancel-button’);
const rangeSelector = this.element.querySelector(‘.rangeSelector’);
const cancel$ = Observable.fromEvent(cancelBtn, 'click');
const range$ = Observable.fromEvent(rangeSelector, 'change').map(e => e.target.value);
const stop$ = Observable.merge(cancel$, range$.filter(x => x > 500))
this.subscription = data$.takeUntil(stop$).subscribe(data => this.updateData(data));
}
onUnmount() {
this.subscription.unsubscribe();
}
}
The first thing you might notice is it’s less code. That’s just one benefit. Another thing that’s happened here is I have composed a stream of stop$ events that kill the data stream. That means when I decide I want to add another condition to kill the data stream, like say a timer, I can simply merge a new observable into stop$. Another thing that is readily apparent is I only have one subscription object that I’m managing imperatively.
首先,代码量变少了。代码当中,create一个stop$事件流,通过takeUntil来终止数据流。如果后续想要再添加另一个条件来终止数据流时,比如一个计时器,就可以简单地将一个新的observable对象合并到stop$中。除此以外,只强制管理一个subscription对象。
Another advantage to this approach is it actually completes the observable. That means there’s a completion event that can be handled anytime you want to kill your observable. If you just call unsubscribe on a returned subscription object, there’s no way you’ll be notified that the unsubscription happened. However if you use takeUntil (or others listed below), you will be notified the observable has stopped via your completion handler.
另一个优点是, 这种方法实际上完成了observable。任何时候都可以用一个完成事件来杀死observable对象。如果只是在返回的subscription对象上unsubscribe,则无法知晓取消订阅的发生。
The last advantage I’ll point out is the fact that you’re actually “wiring everything up” by calling subscribe in one place, this is advantageous because with discipline it becomes much, much easier to locate where you’re starting your subscriptions in your code. Remember, observables don’t do anything until you subscribe to them, so the point of subscription is an important piece of code.
最后一个优点是,通过在一个地方调用subscribe是非常有利的。因为只有这样,在代码中查找订阅的起始位置会变得非常容易。在订阅之前,observables不会做任何事情。
There is one disadvantage here in terms of RxJS semantics, but it’s barely worth worrying about in the face of the other advantages. The semantic disadvantage is that completing an observable is a sign that the producer wants to tell the consumer it’s done, where unsubscribing is the consumer telling the producer it no longer cares about the data.
当然,这个方法在RxJS语义方面也有一个缺点,但是在其他优点面前,它几乎不值得担心。语义上的缺点是,完成一个observable对象是指生产者想要告诉消费者它已经完成了,而取消订阅是消费者告诉生产者它不再关心数据。
There will also be a very slight performance difference between this and just calling unsubscribe imperatively. However, it’s unlikely that the perf hit will be anything noticeable in the mass-majority of applications.
这与直接unsubscribe 在性能上也有细微的差别。
The general rule is that takeUntil should be the last operator in the sequence(otherwise, leak the subscription after it).
一个规则是 takeUntil应该是最后一个操作子。
Other Operators
take(n): emits N values before stopping the observable.takeWhile(predicate): tests the emitted values against a predicate, if it returnsfalse, it will complete.first(): emits the first value and completes.first(predicate): checks each value against a predicate function, if it returnstrue, the emits that value and completes.
Refer:
angular-rxjs-when-should-i-unsubscribe-from-subscription