原文連結: netbasal.com/rxjs-subjec…
本文為 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()
方法。