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.
這種方法的惟一的優(yōu)勢是性能。因為使越少的抽象來完成工作,執(zhí)行起來性能就會越好些。然而,這在大多數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:
另一種方法就是創(chuàng)建父訂閱并添加所有其他訂閱(作為子訂閱),將訂閱合并為單個訂閱。使用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來終止數據流。如果后續(xù)想要再添加另一個條件來終止數據流時,比如一個計時器,就可以簡單地將一個新的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.
另一個優(yōu)點是, 這種方法實際上完成了observable。任何時候都可以用一個完成事件來殺死observable對象。如果只是在返回的subscription對象上unsubscribe,則無法知曉取消訂閱的發(fā)生。
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.
最后一個優(yōu)點是,通過在一個地方調用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語義方面也有一個缺點,但是在其他優(yōu)點面前,它幾乎不值得擔心。語義上的缺點是,完成一個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).
一個規(guī)則是 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