ニートが学ぶUniRx UniRxの仕組み

はいどーもどーも。
https://qiita.com/mattak/items/106dfd0974653aa06fbc
この記事を読んでもいまいちわからない箇所が多かったと「ニートが学ぶUniRx UniRxの成り立ち」で書きました。
例のごとくとりすーぷさんからアドバイスをいただいたので(いつもご丁寧にありがとうございます)、それを頭の中で整理したいと思います。

Operator: Where再び

上記の記事の7項目目である「Operator: Where」を見ていきます。
前回は「OnNextがどうなってるのかわからーん!」ということで終わっていました。
それについて、とりすーぷさんに以下のように教えていただきました。

「メソッドチェーンを記述した時点では、各オペレータがインスタンス化されただけです。末端でSubscribeを実行すると、末端からストリーム源に向かって、順番にSubscribeが実行されてObserverが順番に登録されていきます。
で、メッセージが発行されると、自身のオペレータで処理したのちに、自分のSubscribeを呼び出して登録されたObserverのOnNextを呼んで、次のオペレータに処理が移ります。」

コレを理解するために、まず以下の二点を抑えておきました。

・拡張メソッドでどんどん返すのでメソッドチェーンを作れる
・ObserverはSubscribeで登録される

では順番に見ていきます

メソッドチェーンを記述した時点

「メソッドチェーンを記述した時点では、各オペレータがインスタンス化されただけ」
ここを見ていきます。
メソッドチェーンを記述した時点とは、すなわち

ここですね。
もう少しわかりやすいようにチェーンを繋げていくと

とかでしょうか(例題の記事にTakeは実装されていません)。
このチェーンを繋げたとき「各オペレータがインスタンス化されただけ」だと言うのです。
どういうことか?
ズバリ、ここです。

メソッドチェーンを形成していく上で新しいインスタンスが生成されていますよね。
コンストラクタは以下のようになっています。

これが「メソッドチェーンを記述した時点では、各オペレータがインスタンス化されただけ」の意味です。

末端でSubscribeを実行したとき

次は「末端でSubscribeを実行すると、末端からストリーム源に向かって、順番にSubscribeが実行されてObserverが順番に登録されていきます」を見ていきたいと思います。
末端のSubscribeとはすなわち以下のコードです。

最後にobserverがSubscribeされていますよね(イベントハンドラであるobserverをイベント発行元に登録、というイメージ)。
こうすると「末端からストリーム源に向かって、順番にSubscribeが実行され」るのだそうです。
ここを詳しく見ていきます。

末端のSubscribeを呼んでいるのはWhereObservable型のインスタンスです(オペレータ)。
このクラスのSubscribeは以下のように定義されています。

先程「このクラスのSubscribe」と言ったとおり、オペレータごとにSubscribeの実装は異なります。
これはかなり重要な気がしています!
で、今回のWhereObservable型では何をしているかと言うと、インスタンス生成時に受け取っていたobservableのSubscribeを呼んでいます。
これがまさに「順番にSubscribeが実行されてObserverが順番に登録されていきます」の部分ですね!

で、インスタンス生成時に受け取っていたobservableってなんやねんという話ですが、もう一度拡張メソッドを見ましょう。

ありますね!
メソッドが生えているオブジェクトobservable(例だとsubject)をコンストラクタに渡しています。

チェーンがいくつも連なっている場合、Subscribeが別のクラス(同一である場合もあり)のSubscribeを呼び出します。
そして最後にはSubjectのSubscribeに到達します。
例の場合ですと「SushiLaneSubject」ですね。
こちらも見てみましょう。

引数で渡されている「observer」はWhereObservableの中のSubscribeで作成したObserverです。
このobserverを最終的にはobserversというリストに登録していますね。
これで「末端でSubscribeを実行すると、末端からストリーム源に向かって、順番にSubscribeが実行されてObserverが順番に登録されていきます」が終了です。

メッセージが発行されたとき

ではメッセージが発行されたときの挙動も見ていきましょう。
メッセージが発行されたときとは、すなわちOnNextやOnCompleteなどが呼ばれたとき、ということです。
ここではOnNextだけを見ていきます。

とりすーぷさんによれば、メッセージが発行されると「自身のオペレータで処理したのちに、自分のSubscribeを呼び出して登録されたObserverのOnNextを呼んで、次のオペレータに処理が移ります」とのことです。
これを順番に見ていきますよ。

例はこんな感じ。

SushiLaneSubjectクラスもといSubjectにおけるOnNextの実装はこうなっています。

リストになっているので一つずつOnNextを呼んでいますが、今回は登録しているものが一つなので、単にobserverのOnNextを呼ぶようになっている、という理解でも問題なさそうです。
nextは「すずき」で、observerは先ほど登録したもの(すなわちWhereObservableのSubscribe中で作成したobserver)です。
中身はこんな感じ。

nextは「すずき」ですね。
Whereオペレータで「neta == “まぐろ”」をチェックし(this.operation(next)の部分)、これが真である場合のみ次のOnNextを呼びます。
今回は偽なのでダメですね。

次のOnNextも見てみましょう。
今度は「まぐろ」です。
これは真なので、次のOnNextが呼ばれます。
このときnextすなわち「まぐろ」は次のOnNextに渡されています。
次のOnNextとはすなわち「var observer = new SushiObserver();」コイツです。
Subscribeするときに渡したやつですね。

中身はこんな感じ。

単にデバッグログを表示しておしまいですね。
今回だと「まぐろおいしいです (^q^)」となります。

これで「メッセージが発行されると、自身のオペレータで処理したのちに、自分のSubscribeを呼び出して登録されたObserverのOnNextを呼んで、次のオペレータに処理が移ります。」も終了したことになります。

IObserverが入力、IObservableが出力みたいなもん

その後もありがたいことにとりすーぷさんからアドバイスをいただいています。
曰く「IObserverが入力、IObservableが出力みたいなもん」らしいです。
正確な引用は以下の通り。

要するに、IObserverが入力、IObservableが出力みたいなもんですねー。で、出力に次の入力をつなげるのがSubscribeです! 普段使ってる、ラムダ式で関数登録してるSubscribeも実はただのシンタックスシュガーであって、内部でAnonymousObserver的なのを生成してますよ。

確かにSubscribeするときIObserverを入力として受け取ります。
OnNextで最終的に出力するのはIObaservableの役割です。
また、最終的な出力につなげるためにはSubscribeを連鎖させていかなければなりません。

という認識で大丈夫ですかね?
あんまり自信ありませんw

終わりに

丁寧に追っていったので、UniRxの仕組みの理解が随分進んだと思っています。
この調子で他の入門講座も読んでいこうと思いますよ。
しかしそろそろ実践でも使いたい……。

ほなそんな感じでまた。

フォローする