RxJS における Subject の活用
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 の挙動に関しては公式のガイドを確認してください。
Discussion