😊

RxJS における Subject の活用

2022/03/09に公開

RxJS の Subject

RxJS の Subject は Observable , Observer 両方の性質を持つオブジェクトです。

通常、Observable は 生成されたタイミングで(Observable の引数によって)流す値が決まっており、
後から流す値を追加したり変更したりするには、pipe などを利用して、新しい Observable を作成する必要があります。

Subject は subscribe に加えて、 next, error, complete のメソドを利用することが可能であり、
任意のタイミングで 複数の subscriber に自由に値を送信することが可能です。

以下の例では、 console.log と console.error に 1,2 の値がそれぞれ流れます。

import { Subject } from 'rxjs';
 
const subject = new Subject();

subject.subscribe(console.log);
subject.subscribe(console.error);

subject.next(1);
subject.next(2);

Subject はその性質から hot/multicast なObservable として機能します。
そのためもあってか、subscribe のタイミングによって取得できる値には違いが出てきます。

以下の例では、console.log には 1,2 両方の値が流れますが、
console.error には 2しか流れません。

import { Subject } from 'rxjs';
 
const subject = new Subject();

subject.subscribe(console.log);
subject.next(1);

subject.subscribe(console.error);

subject.next(2);

Subject はそれ自体が observer であるため、 subscribe の引数として利用することも可能です。

Subject に複数の subscribe を追加して、まとめて Observable に 与えることで、
シンプルに multicast を実装できます。

以下の例では、observable fired のログは1回限りで、
console.log / console.error 両方に値を流すことが可能です。

import { Observable, Subject } from 'rxjs';

const obs$ = new Observable((sub) => {
  console.log('observable fired');
  sub.next('1');
  sub.next('2');
  sub.complete();
});

const subject = new Subject();
subject.subscribe(console.log);
subject.subscribe(console.error);

obs$.subscribe(subject);

Subject の加工

Subject は Observable であるため、
以下のようにして pipe で流れてくる値を変換することも、もちろん可能です。

import './style.css';

import { Observable, of, Subject } from 'rxjs';
import { map } from 'rxjs/operators';

const subject = new Subject();

subject.subscribe(console.log);

const obs_en$ = subject.pipe(map(name=>`hello,${name}`))
const obs_fr$ = subject.pipe(map(name=>`cava,${name}`))

obs_en$.subscribe(console.error);
obs_fr$.subscribe(console.error);

subject.next("Taro")

明示的に Observable への変換を行いたい場合は、 subject.asObservable() をコールすることも可能です。

サービスの内部で Subject を利用しながら、外部に readOnly な Observable を公開したいケースなどで便利です。

様々な Subject

標準の Subject の他にもいろいろな特徴を持った Subject が実装されています。

BehaviorSubject

BehaviorSubject は、内部で一度だけ直近の値を保持します。
この挙動によって、BehaviorSubject は、subscribe を受けた瞬間に必ず何かしらの値を得るようになっており、
宣言時に 初期値をコンストラクタで渡す必要があります。

import { BehaviorSubject } from 'rxjs';
const subject = new BehaviorSubject(0);

subject.subscribe(console.log);

subject.next(1);
subject.next(2);

subject.subscribe(console.error);

subject.next(3);

上記の例では、console.error は 2,3 と2つの値を受け取ります。

ReplaySubject

ReplaySubject は内部で保持する値の個数を指定できます。

以下の例では console.error は 2,3,4,5を受け取ります。

import { ReplaySubject } from 'rxjs';
const subject = new ReplaySubject(3); 

subject.subscribe(console.log);

subject.next(1);
subject.next(2);
subject.next(3);
subject.next(4);

subject.subscribe(console.error);

subject.next(5);

その他の Subject

その他の Subject の挙動に関しては公式のガイドを確認してください。

https://rxjs.dev/guide/subject

Discussion