原文鏈接: https://netbasal.com/rxjs-subjects-for-human-beings-7807818d4e4d
本文為 RxJS 中文社區 翻譯文章,如需轉載,請注明出處,謝謝合作!
如果你也想和我們一起,翻譯更多優質的 RxJS 文章以奉獻給大家,請點擊【這里】
我已經發表過一篇關于 Subject 的文章 (中文),但這次我想嘗試一種不同的方式。
要理解 Subject
是什么的最簡單的方式就是重新創建一個。我們來創建一個簡易版的 Subject
。
注意: 下面的示例只是為了闡述概念,還不足以應用于實際開發之中,還有它們并不是 Rx 中 Subjects 的真正完整實現。
我們來看看真相。
Subject 既是 Observable,又是 Observer 。
Subject 是 Observable
這表示它擁有所有的操作符 (map
、filter
,等等) 并且你可以訂閱它。
class MySubject extends Rx.Observable {
constructor() {
super();
}
}
這是第一部分所需的一切了。它可以通過擴展 Observable
類成為 Observable
。
Subject 是 Observer
這表示它必須實現 next()
,error()
和 complete()
方法。
class MySubject extends Rx.Observable {
constructor() {
super();
}
next() {}
error() {}
complete() {}
}
好了,我們來看下一個真相。
Subject 可以扮演源 observable 和 眾多觀察者之間的橋梁或代理,使得多個觀察者可以共享同一個 observable 執行。
class MySubject extends Rx.Observable {
constructor() {
super();
this.observers = [];
}
subscribe(observer) {
this.observers.push(observer);
}
next(value) {
this.observers.forEach(observer => observer.next(value));
}
error(error) {
this.observers.forEach(observer => observer.error(error));
}
complete() {
this.observers.forEach(observer => observer.complete());
}
}
當你調用 subscribe()
方法時,僅僅是將 observer
添加到一個數組中。next()
、error()
和 completed()
方法會調用數組中每個 observer
的對應方法。
來使用我們的 Subject 。
const interval$ = Rx.Observable.interval(1000).take(7);
const subject = new MySubject();
subject.map(value => `Observer one ${value}`).subscribe(value => {
console.log(value);
});
interval$.subscribe(subject);
setTimeout(() => {
subject.map(value => `Observer two ${value}`).subscribe(value => {
console.log(value);
});
}, 2000);
當使用 Subject
時,無論你何時 subscribe
, 你永遠都會得到相同的執行,這點不同于典型的 observable,每次 subscribe
都會開啟有個新的執行。(在我們的案例中,這表示你會有兩個不相關的 intervals)
Subject 讓你同享相同的 observable 執行
我們來總結一下這里發生了什么。
當對 subject 調用 subscribe
時,只是將 observer
添加到數組中。
當 subject
扮演 observer
時,每當源 observable (在我們的案例中是指 interval
) 發出值時,它會調用數組中每個 observer
的 next()
方法。
BehaviorSubject
現在讓我們來嘗試實現 BehaviorSubject
的簡易版。
我們來看看真相。
-
BehaviorSubject
需要一個初始值,因為它必須始終返回一個訂閱值,即使它還沒接收到next()
調用。 - 被訂閱后,它會返回 subject 的最新值。
- 無論在任何時候,你都可以在非 observable 的代碼中使用
getValue()
方法來獲取 subject 的最新值。
class MyBehaviorSubject extends Rx.Observable {
constructor(initialValue) {
super();
this.observers = [];
if (typeof initialValue === 'undefined') {
throw new Error('You need to provide initial value');
}
this.lastValue = initialValue;
}
subscribe(observer) {
this.observers.push(observer);
observer.next(this.lastValue);
}
next(value) {
this.lastValue = value;
this.observers.forEach(observer => observer.next(value));
}
getValue() {
return this.lastValue;
}
}
來使用我們的 BehaviorSubject
。
const subject = new MyBehaviorSubject('initialValue');
subject.map(value => `Observer one ${value}`).subscribe(function(value) {
console.log(value);
});
subject.next('New value');
setTimeout(() => {
subject.map(value => `Observer two ${value}`).subscribe(function(value) {
console.log(value);
});
}, 2000);
ReplaySubject
現在讓我們來嘗試實現 ReplaySubject
的簡易版。
我們來看看真相.
-
ReplaySubject
表示一個對象既是 observable 序列,又是 observer 。 - 每次通知都會廣播給所有已經訂閱和未來的 observers,observers 會遵循緩沖調整策略。
class MyReplaySubject extends Rx.Observable {
constructor(bufferSize) {
super();
this.observers = [];
this.bufferSize = bufferSize;
this.lastValues = [];
}
subscribe(observer) {
this.lastValues.forEach(val => observer.next(val));
this.observers.push(observer);
}
next(value) {
if (this.lastValues.length === this.bufferSize) {
this.lastValues.shift();
}
this.lastValues.push(value);
this.observers.forEach(observer => observer.next(value));
}
}
來使用我們的 ReplaySubject
。
const subject = new MyReplaySubject(3);
subject.next('One');
subject.next('Two');
subject.next('Three');
subject.next('Four');
setTimeout(() => {
subject.map(value => `Later Observer ${value}`).subscribe(function(value) {
console.log(value);
});
}, 2000);
何時使用 Subject
- 需要共享相同的 observable 執行。
- 當需要決定觀察者遲來時該怎么做,是否使用
ReplaySubject
、BehaviorSubject
? - 需要完全控制
next()
、error()
和completed()
方法。