歌舞伎座tech発表資料 RxJSの中を追う

Post on 15-Jul-2015

2.448 views 0 download

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時の チェーン生成時

チェーン生成の スレッドを変えられる

何に使うんだろ?

以上です

ご清聴ありがとうございました