[譯] RxJS: 白話 Subjects

SangKa發表於2017-08-03

原文連結: netbasal.com/rxjs-subjec…

本文為 RxJS 中文社群 翻譯文章,如需轉載,請註明出處,謝謝合作!

如果你也想和我們一起,翻譯更多優質的 RxJS 文章以奉獻給大家,請點選【這裡】

我已經發表過一篇關於 Subject 的文章 (中文),但這次我想嘗試一種不同的方式。

要理解 Subject 是什麼的最簡單的方式就是重新建立一個。我們來建立一個簡易版的 Subject

注意: 下面的示例只是為了闡述概念,還不足以應用於實際開發之中,還有它們並不是 Rx 中 Subjects 的真正完整實現。

我們來看看真相。

Subject 既是 Observable,又是 Observer 。

Subject 是 Observable

這表示它擁有所有的操作符 (mapfilter,等等) 並且你可以訂閱它。

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);
複製程式碼

[譯] RxJS: 白話 Subjects

當使用 Subject 時,無論你何時 subscribe, 你永遠都會得到相同的執行,這點不同於典型的 observable,每次 subscribe 都會開啟有個新的執行。(在我們的案例中,這表示你會有兩個不相關的 intervals)

Subject 讓你同享相同的 observable 執行

我們來總結一下這裡發生了什麼。

當對 subject 呼叫 subscribe 時,只是將 observer 新增到陣列中。

subject 扮演 observer 時,每當源 observable (在我們的案例中是指 interval) 發出值時,它會呼叫陣列中每個 observernext() 方法。

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);
複製程式碼

[譯] RxJS: 白話 Subjects

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);
複製程式碼

[譯] RxJS: 白話 Subjects

何時使用 Subject

  • 需要共享相同的 observable 執行。
  • 當需要決定觀察者遲來時該怎麼做,是否使用 ReplaySubjectBehaviorSubject
  • 需要完全控制 next()error()completed() 方法。

MediumTwitter 關注我,以閱讀更多 Angular、Vue 和 JS 相關內容!

相關文章