[譯] RxJS: 白話 Subjects

原文鏈接: https://netbasal.com/rxjs-subjects-for-human-beings-7807818d4e4d
本文為 RxJS 中文社區(qū) 翻譯文章,如需轉(zhuǎn)載,請(qǐng)注明出處,謝謝合作!
如果你也想和我們一起,翻譯更多優(yōu)質(zhì)的 RxJS 文章以奉獻(xiàn)給大家,請(qǐng)點(diǎn)擊【這里】

header.png

我已經(jīng)發(fā)表過(guò)一篇關(guān)于 Subject 的文章 (中文),但這次我想嘗試一種不同的方式。

要理解 Subject 是什么的最簡(jiǎn)單的方式就是重新創(chuàng)建一個(gè)。我們來(lái)創(chuàng)建一個(gè)簡(jiǎn)易版的 Subject

注意: 下面的示例只是為了闡述概念,還不足以應(yīng)用于實(shí)際開(kāi)發(fā)之中,還有它們并不是 Rx 中 Subjects 的真正完整實(shí)現(xiàn)。

我們來(lái)看看真相。

Subject 既是 Observable,又是 Observer 。

Subject 是 Observable

這表示它擁有所有的操作符 (map、filter,等等) 并且你可以訂閱它。

class MySubject extends Rx.Observable {
 
 constructor() {
    super();
 }
 
}

這是第一部分所需的一切了。它可以通過(guò)擴(kuò)展 Observable 類成為 Observable 。

Subject 是 Observer

這表示它必須實(shí)現(xiàn) next()error()complete() 方法。

class MySubject extends Rx.Observable {
 
 constructor() {
   super();
 }

 next() {}
  
 error() {} 
  
 complete() {}
 
}

好了,我們來(lái)看下一個(gè)真相。

Subject 可以扮演源 observable 和 眾多觀察者之間的橋梁或代理,使得多個(gè)觀察者可以共享同一個(gè) observable 執(zhí)行。

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());
 }
 
}

當(dāng)你調(diào)用 subscribe() 方法時(shí),僅僅是將 observer 添加到一個(gè)數(shù)組中。next()、error()completed() 方法會(huì)調(diào)用數(shù)組中每個(gè) observer 的對(duì)應(yīng)方法。

來(lái)使用我們的 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.gif

當(dāng)使用 Subject 時(shí),無(wú)論你何時(shí) subscribe, 你永遠(yuǎn)都會(huì)得到相同的執(zhí)行,這點(diǎn)不同于典型的 observable,每次 subscribe 都會(huì)開(kāi)啟有個(gè)新的執(zhí)行。(在我們的案例中,這表示你會(huì)有兩個(gè)不相關(guān)的 intervals)

Subject 讓你同享相同的 observable 執(zhí)行

我們來(lái)總結(jié)一下這里發(fā)生了什么。

當(dāng)對(duì) subject 調(diào)用 subscribe 時(shí),只是將 observer 添加到數(shù)組中。

當(dāng) subject 扮演 observer 時(shí),每當(dāng)源 observable (在我們的案例中是指 interval) 發(fā)出值時(shí),它會(huì)調(diào)用數(shù)組中每個(gè) observernext() 方法。

BehaviorSubject

現(xiàn)在讓我們來(lái)嘗試實(shí)現(xiàn) BehaviorSubject 的簡(jiǎn)易版。

我們來(lái)看看真相。

  • BehaviorSubject 需要一個(gè)初始值,因?yàn)樗仨毷冀K返回一個(gè)訂閱值,即使它還沒(méi)接收到 next() 調(diào)用。
  • 被訂閱后,它會(huì)返回 subject 的最新值。
  • 無(wú)論在任何時(shí)候,你都可以在非 observable 的代碼中使用 getValue() 方法來(lái)獲取 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;
  }


}

來(lái)使用我們的 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);
BehaviorSubject.gif

ReplaySubject

現(xiàn)在讓我們來(lái)嘗試實(shí)現(xiàn) ReplaySubject 的簡(jiǎn)易版。

我們來(lái)看看真相.

  • ReplaySubject 表示一個(gè)對(duì)象既是 observable 序列,又是 observer 。
  • 每次通知都會(huì)廣播給所有已經(jīng)訂閱和未來(lái)的 observers,observers 會(huì)遵循緩沖調(diào)整策略。
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));
  }
}

來(lái)使用我們的 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);
ReplaySubject.gif

何時(shí)使用 Subject

  • 需要共享相同的 observable 執(zhí)行。
  • 當(dāng)需要決定觀察者遲來(lái)時(shí)該怎么做,是否使用 ReplaySubject、BehaviorSubject?
  • 需要完全控制 next()error()completed() 方法。

MediumTwitter 關(guān)注我,以閱讀更多 Angular、Vue 和 JS 相關(guān)內(nèi)容!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容