歌舞伎座tech発表資料 RxJSの中を追う
Transcript of 歌舞伎座tech発表資料 RxJSの中を追う
RxJSの中を追う
安福 一樹 (wilfrem, @WilfremLuminous)
自己紹介
名前: 安福 一樹 HN: wilfremとか@WilfremLuminous
仕事 Webエンジニア。 主にサーバサイドの開発をしています。 最近新部署に異動しました。
趣味 最近は面白技術を色々試して遊んでいる
はじめに
今日のタイトルについて
RxのHot/Coldを入り口に Rxが中で何をやっているかを追いたいと思います。
主に細かなところについて。
Rxにはどうしてもハマる点あるのですが Rxが中で何をやっているかが分かれば ハマりどころを回避できると思います。
対象プラットフォームについて
RxJS中心のお話になっていますが Rx.NET/RxJavaの実装と見比べてみたところ 言語ごとの実装に大きな違いはなく
Rx.NET/RxJavaでも使える話になります
スケジューラーしか大きな違いが無かった オペレータ名が微妙に違うのも LINQ引きずってる.NETだけの話だし
本編
まずはこの問題のコードを御覧ください
“side effect”は何回表示されるか?
var rx = require("rx"); var s = new rx.Subject(); var stream = s.tap(function(){console.log("side effect");}); stream.subscribeOnNext(function(){}); stream.subscribeOnNext(function(){}); s.onNext("foo");
ちょっと読みにくいので図にします
図にしてみた
side effectは何回表示されるでしょうか?
subject tap(do)
side effect message
onNext
nop
nop
答え
2回
subject tap(do)
side effect message
onNext
nop
nop side effect
なぜ二回なのか?
よくある説明 「doはColdなObservableだ」
→とても表面的な説明と理解 説明が良くない
現象の正しい理解のために Rxの動作原理を追ってみる
Rxのおさらい
Rxの基本要素
Rxのストリームと3種のメッセージ
onNext* (onCompleted|onError)?
onNext onNext onNext onCompleted
ObservableとObserver
onNext(msg: T)
onCompleted()
onError(error: any)
Observer<T> Observable<T>
subscribe(o: Observer<T>)
1. subscribeする
2. つながる
3. messageがpushされる
Observableとオペレータ
map filter throttle
Observable map
オペレータ=Observableを返す関数
RxにはObservableのメンバに様々なオペレータがある
Observable
オペレータのチェーン
map filter throttle Observable
Rxのオペレータは繋げられる
メッセージはオペレータに”加工”されて伝達する(onCompleted/onErrorも同様)
map filter throttle Observable Observer
message
onNext onNext onNext onNext
message subscribe
ところで……
オペレータはObservableである
オペレータを重ねられる
subscribeできる
→Observableである
Observable map
オペレータはObservableである
Observable
オペレータはObserverでもある?
Observableに繋げられる
メッセージを受け取れる
→Observerである
Observable map
オペレータはObserverでもある
Observer
message
Observer?いた?
しかし、オペレータの利用で
Observerを見かけることはない
Observerはどこにいるのか?
改めて: ObservableとObserver
onNext(msg: T)
onCompleted()
onError(error: any)
Observer<T> Observable<T>
subscribe(o: Observer<T>)
1. subscribeする
2. つながる
3. messageがpushされる
当然ですが、オペレータ=Observable
何度も説明したとおり、オペレータ=Observable
つまり、後ろのObserverにsubscribeされる
もちろん、後ろがオペレーターなら オペレーターがオペレーターをsubscribeする
どうやって……?
map filter throttle Observable Observer
subscribe
オペレーターがオペレーターを
Subscribeする方法とは?
答え: オペレータはObserverを生成する
Observerを「生成」する
1つ手前のObservableをsubscribeする
subscribeはチェーンする
map filter throttle Observable Observer
subscribe
throttleObserver
Subscribeが完了すると
最終的にObservableにチェーンが到達
結果的にSubscribeすると Observerのチェーンが生成される
map filter throttle Observable Observer
メッセージ メッセージ メッセージ
オペレータの内部実装
ObservableBase(的な物)がある =AnnonimousObservable
パフォーマンス改善verだと まさにObservableBaseが存在する
subscribeだけ差し替えられる
各オペレータはsubscribeの差し替えを実装
差し替え処理で手前をsubscribeしている
Rxの”Hot”とは?
Hot = Observerのチェーンを作ること。
Observerのチェーンを作るから メッセージが流れる
RxのHotとは subscribeしてチェーンを作成すること。
チェインの行き着く先について
チェインの行き着く先
一番先頭のObservableはどういう物か?
2種類のObservableが存在する
map filter throttle Observable Observer
?
タイプ1: “非保持型”
特徴 subscribeしてきたObserverを保持しない
何回subscribeしても同じ結果となる →例えばチェーンを何度も生成
例 just, create、大抵のオペレータなど
=”Cold Observable”、Coldな性質と呼ばれる
タイプ2: “保持型”
特徴 subscribeしてきたobservableをキャプチャする
必要になった時にメッセージを流す
流す対象をリストとして持ち、分配機能を持つ
例 publish, fromEvent, Subject
= “Hot Observable”、Hotな性質と呼ばれる
Hot/Coldが分かりにくいのは 用語間違っているからな気がしている
話をだいぶ戻して
side effectはなぜ2回表示されるか?
subject tap(do)
side effect message
onNext
nop
nop
もう分かりますよね。
こうなるから
subject tap(do)
side effect
message
onNext
nop
nop
side effect
tap observer
tap observer
では?
side effectを1回だけ表示させるには?
subject tap(do)
side effect message
onNext
nop
nop
答え
subjectを挟む
subject tap(do)
side effect message
onNext
nop
nop
subject
Subjectの役割
役割 SubjectはObserverをリストとして持つ
Observerとしてメッセージを受け取る
Observableとしてメッセージを配信する
Subjectを挟むと”保持型”になる =Cold→Hot変換の正体 publish, share, fromeventは 内部でSubject使っている
Cold→Hot変換のconnect()とは
Cold→Hot変換すると出てくる
ConnectableObservable
中にSubjectがいる。
ConnectableObservable = Subject
ConnectableObservable#connect()とは 内部のsubjectにsubscribeさせること。
改めて、Rxは中で何をやっているのか?
復習を兼ねて
Rxの構成要素
Rxは (乱暴めに言うと) 5つの要素で構成されている
Observable Observer
ObserverとObservable
BaseObservableと オペレータ
subject operator
SubjectとHot
onNext* (onCompleted | onError)?
scheduler
Scheduler
おまけで解説します
ストリーム
Observableとオペレーター
Observableに Operatorを繋げて 次のObservableを得られる
Observable
Observable operator
Observable
Observableとオペレーターの関係
オペレータは 手前の参照を持つ
後ろの参照はしない
メッセージは まだ流れない
この状態=Cold
Observable operator
参照
参照せず
Operatorの仕組み
オペレータはほぼ処理が共通 =ObservableBase
subscribeを差し替えられるのみ
Operator extends ObservableBase<T>
subscribe(o: Observer<T>)
差し替え可能
Rxのオペレータは多数存在するが 大半はここの差し替えの違いのみ
subscribe差し替えの例
filter
条件に一致した時に 次にメッセージを流すObserverを 作成する
map
メッセージを変換して流す Observerを作成する
subscribeの連鎖
Subscribe連鎖
Subscribeで 後ろのObserverを知る
Observerのチェーンが作成される
Observable operator
subscribe
Observer
Observer
参照
ストリームの作動
連鎖が完了 →Observerのチェーンが完成
これで「作動」し メッセージが流れる →Hotになる
Observable operator Observer
Observer メッセージ
Cold “Observable”とHot “Observable”
Cold “Observable”
Observer非保持
オペレータなら subscribeごとに チェーンを生成
Hot “Observable”
Observer保持
分配機能を持つ
Subjectでできている
メッセージの伝搬
Observer
onNext()
onCompleted()
onError()
Observer
onNext()
onCompleted()
onError()
message message message
メッセージはSubscribeに到達する
Observer
onNext()
onCompleted()
onError()
subscriber
subscribeOnNext()
subscribeOnCompleted()
subscribeOnError()
message message
ストリームの終了
disposeを呼ぶか ストリームが終了で チェーンは消滅 Observable operator Observer
Observer
Observable operator Observer
dispose()
以上がRxの中身になります
おまけ: Schedulerについて
流れ的に入らなかった……
スケジューラーとは
実行する関数をイベントキューに積む機構
メッセージの送信タイミングを変えられる
スレッドも変えられる
関数
イベントキュー
スレッド・イベントループ
関数
関数
キューに積む 実行
Schedulerの実装
Rxの実装方法は言語によって違いはない
例外がScheduler
スレッドが使えるかどうか? イベントループはあるか?
Rx.NET/RxJavaのScheduler
出来ること スレッド切り替え(新規スレッド・ワーカー)
スレッド同期
イベントキューへの積み込み
etc.
大体の物は用意されている
RxJSにおけるScheduler
イベントループに積む/積まないのみ。 シングルスレッドですし……
Scheduler.defaultが使える
積む関数は適切に選ばれる node.jsはsetImmediate→nextTick
ブラウザはsetImmediate→MessageChannnel→
postMessage→onReadyStateChanged→ setTimeout
スケジューラーの使いどころ
重たい処理
処理を一息つかせたい時
スレッド切り替え/同期したい時
observeOnとsubscribeOnの違い
observeOn
メッセージが来た時
メッセージ処理に イベントループや スレッド切り替えを挟む
subscribeOn
subscribe時の チェーン生成時
チェーン生成の スレッドを変えられる
何に使うんだろ?
以上です
ご清聴ありがとうございました