東京のWeb制作会社LIGLIGならできるおもしろいこと。フロントエンドシェアこんにちは、王です。みなさん、「RxJS」をご存知でしょうか?すごく大雑把に言うと、RxJSとは非同期処理(マウスクリックなどのイベントベースの処理も含める)をするときに超絶便利なライブラリです!イメージしやすいように「便利」とは言いましたが、決して「便利」という言葉ではおさまらないくらいのインパクトがあると思います。した「イノベーション」に近い感覚です。今回は、RxJSを全く知らない初心者でもとっつきやすいように説明してみたいと思います。Extensions)」(以下「RX」)というC#発のプロジェクトがありますが、RxJSはそのJS版に当たります。これだけで、このライブラリの堅牢性と信頼性の高さがうかがえます。今では、「Java」「JavaScript」「Scala」「C++」「Ruby」「Python」「Groovy」「JRuby」「Kotlin」など、さまざまな言語の移植版が開発されています。非同期処理あるいはイベントベースのプログラムは、実行時間が保証されないため、大量に用いるとプログラムが錯綜してくる場合が多いです。依存関係にある非同期処理の場合、ネストが深まり、いわゆる「コールバック地獄」になりがちでコードの可読性が著しく下がってしまいます。何がどうなっているのかわからなくなってしまうだけでなく、エラーハンドリングもしづらくなります。言え、非同期処理を必要とする時代の風向きは依然として変わる気配がないようです......。重要性が増しているように思えます。そんな厄介な非同期処理をどうにかすっきりさせようと、「Async.js」のようなライブラリが出てきたり、「Promises/A+」という仕様が考案されたり(すでにネイティブでもサポートされました)で、非同期処理の諸問題がある程度改善されてきました!数多くある非同期処理を援助するライブラリの中でも、RxJSは独自の思想を持った異色の存在です。他のライブラリと比べ物にならないほど柔軟に扱うことができて、昨今もてはやされているReactive-Functional-Programmingとも親和性が高く、これからの時代にマッチしたライブラリと言えるでしょう。公式サイトでは以下のように記述されています。パターン」、「関数型プログラミング」のアイディアをふんだんに取り入れたライブラリです。RXの根底には「Observerパターン」が根付いています。RXはObserverパターンの考え方を取り入れつつ、イベントやデータをストリームとして扱えるような仕組みを導入することによって、プログラミングの柔軟性と可読性を飛躍的に上昇させました。冒頭でこんな抽象的なことを言われてもピンとこないと思いますが、今はあまり気にする必要はありません。このまま読み進めてみてください。説明や実例を見ながら、徐々に全体像が見えてくると思います。なってからまた読み返していただければと思います。そもそも「Observerパターン」と「Iteratorパターン」を知らないのであれば、以下の記事のご一読をおすすめします。Observerパターンウォーミングアップいきなり核の部分に触れる前に、まずはRXの世界観を少しだけ体感していただきたいです。いうことで、手を動かしてみよう!イベントを配列のように操る?まずは下記のコードを見てください。7]これは、配列のfilterメソッドを使って1から8までの奇数のみを選び出した例です。簡単ですよね?では、RxJSを使って同じ操作を配列の代わりに「イベント」に対してやってみます。下のデモをいじってみてください。ボタンをクリックしても何も出てきませんが、「alt」キーを押しながらクリックするとコンソールのほうにログが吐き出されるようになっています。});配列と同じ名前のfilter()メソッドを使っているのが、なんとなくわかると思います。「イベントは配列に似ているよね!」と言ったら、どう思いますか?もしかしたら「えっ?全く別ものなのでは?」と思うかもしれません。「イベント」という言葉だけにフォーカスしてしまうと、確かに配列とは別物です。ところが、違う視点から見てみるとイベントとは時間軸上に点在する離散的な「点」ですよね。タイムライン上にある「個々のイベントの集合体」が配列に似ているということです。一言で言うと「配列のようにイベントを操作することができる」のが嬉しいですね。それらはすべて「イベント」に適用できるところが大きな可能性を秘めているんです。さっきの例でやったことは、ボタンの「クリックイベントの配列」に対して、filter()メソッドを適用し、その結果、クリック時に「altが押されているイベント」だけが残った新しい「イベントの配列」が生成されます。出来上がった新しい「イベントの配列」を一個ずつ取り出すためには、subscribe(購読)を行う必要がありますので、subscribeメソッドを使い、値を出力しました。ここまで説明したら、filter()の使い方がわかる方なら、もう十分理解できたのではないかと思います。直観的に理解してもらえるように説明用のプログラムを作りました。Bin大まかなプロセスは以下の通り。購読者に通知するStream(ストリーム)ここまで「イベントの配列」と連発してきましたが、厳密には「配列」ではないのは明白です。わかりやすいように、あえて「イベントの配列」という言い方をしてきました。実は、ここでいう「イベントの配列」にはもっとかっこいい名前があります。それが「Stream(ストリーム)」です。「Stream」とは「水の流れ・小川」という意味ですから、「タイムライン」という「川」が流れていて、時折ドンブラコ〜ドンブラコ〜と「イベント(データ・値)」が流れてきます〜といったイメージです。ストリームです!Rxでは、しばしばObservable/Observable-Sequence(後述)と呼ぶことがありますが、やはり「ストリーム」のほうがわかりやすいので、以降「ストリーム」と記述させていただきます。Rxではストリームを作成するメソッドはたくさん用意されています。Observerの「OnNext/onError/onCompleted」メソッドを用いて、一からプログラミング的にObservableを作成。Observerが購読する時点で動的にObservableを生成。限定された挙動を持つObservableを作成。配列などの他のデータ型をObservableに変換する。指定された時間間隔で値を発行するObservableを作成。引数を1つだけ取って、取った引数を唯一の値にするObservableを作成。一連の整数を生成するObservable。ある値あるいは一連の値を繰り返し発行するObservable。関数の戻り値を値とするObservableを作成。指定した遅延時間後に値を生成する。周期の指定がある場合は一定周期で値を発行する。ストリームを操作する「オペレーター」まだ「filter」しか触れてませんが、「map」「reduce」「merge」「concat」「zip」など、RxJSには他にもこういったストリームを操作するメソッドがたくさん用意されています。いったストリームに対して何らかの操作を行うメソッドのことを「オペレーター」と言います。ストリーム(Observable)を作って「オペレーター」をどんどんかけて、出来上がったストリームを購読(subscribe)する、というのが通常の使い方の流れです。先ほどの例ですと、Rx.Observable.fromEvent()の戻り値がObservableです。このObservableに対してオペレーター(filter)をかけて、最後にsubscribeで値を受け取るようにしています。詳しくは、以下のオペレーターのカテゴリ別レファレンスをご参考ください。Categories:http://xgrommx.github.io/rx-book/content/getting_started_with_rxjs/creating_and_querying_observable_sequences/operators_by_category.htmlオペレーションチェーン配列ではこんな感じで「メソッドチェーン」ができて、簡潔でわかりやすい記述が可能です。`forEach`は`subscribe`のエイリアスです。49もちろん、配列ではないので、1個ずつ順番に値が出力されます。最後の「|」はストリームの「終点」を表しています。このようなグラフを「Marble-Diagrams」と呼びます。オペーレーターを理解するのにとても役に立ちます。このように、オペレーターの戻り値が「Observable」になっていますので、チェーンすることができます。`subscribe()`に渡すためのobserverを作成する。Bin今までは説明しませんでしたが、subscribe()には2種類の引数の渡し方があります。1つ目は直接関数オブジェクトを渡す。);2つ目はobserverオブジェクトを渡す(上のサンプルはこちらのほうです)。o.subscribe(observer);コールバックは3つありますが、いずれも省略できます。1つ目の渡し方は内部的にはやはりobserverオブジェクトを作っています。ちなみに、コールバックの名前が「onNext」になっているのはイテレータパターンの名残です(Observableは一種のコレクションですから)。使って、Observableオブジェクトを生成し、source変数に格納します。対して値を発行するようになっています。呼ばれたときに実行されます。実行すると、500ミリ秒ずつログが出力され、5秒後にログの出力が止まります。Bin上記サンプルコードでは「onNext」しか使っていませんが、状況に応じて以下の3つのメソッドを使って、「Next/Error/Completed」シグナルを発生させることで購読者に通知を行います。Observable実は、Observableには「Cold」と「Hot」の2種類があります。本セクションでは、この2種類のObservableの違いと使い分けについて説明します。ログを見てみると、各Observerは新しいシーケンスから値を受け取っていることに気づくはずです。個々のObserverが「自分だけの」Observableから値を受け取っているのです。Observable(hot)から最新の値を受け取ります。使って、ソースとなるObservable(source)からプッシュしてきた値をさらに自身(hot)が持つobserverたちにプッシュするようにします。Observable」は、複数のObserverに対して同じタイミングで同じ値を発行する「1対マルチ」なスタイルのObservableです。このような挙動は後ほどで紹介するSubjectによって実現しています。受け取った値がhotのobserverたちにプッシュされるようになる。ついてSubjectは、RXを使う上でもう1つの重要なクラスです。Subjectクラスは、ObservableとObserverの両方を継承しています。SubjectはObservableでありながら、Observerでもあるということです。ObserverとObservableの顔を併せ持つSubject以下の例では、Subjectを1つ作って、このsubjectに対して、subscribeします(Observableの特性)。同じsubjectを使って、自身に登録してあるobserverに対して値をプッシュします(observerの特性)。することで、「発行(publish)」と「購読(subscribe)」を1つのオブジェクトに集約することができます。onCompletedsubscription.dispose();「ブロードキャスタート」としてのSubjectSubjectの用途の1つとして、「ブロードキャスト」が挙げられます。SubjectはObservableと同様に、subscribe()インタフェースを持っています。Subjectのsubscribe()は、schedulerを気にしていません。このため、同じObservableに対して、何回もsubscribe()するよりも、subjectに置き換えた方がパフォーマンスがいいということです。購読しつつ、自分のニーズで合わせて自ら値を発行することができます。上記のサンプルコードの最後に下記を追加して確認しましょう。ついてRxには「Scheduler」というクラスがあります。「いつ購読が始まるか」「いつ値が発行されるか」を決めるために用いられます。普通に利用している分には特に気にする必要はないのですが、一応簡単に説明しておきます。下記のコードを見てください。EOF出力の順番は読者の予想通りだ思います。図解でもう一度見てみましょう。コメントを外して、購読タスクを「timeoutスケジュール」上で実行するように指定しますと......。completed!なぜこうなったかと言うと、subscribeの処理を別スレッド(厳密に言うと次のイベントループ)に回したからです。擬似的にマルチスレッドっぽい挙動にすることができます。「timeoutスケジューラ」とは言っても、実際はランタイムによって利用している関数が違ってきます。利用する関数の優先順位は以下の通り。readystatechangedsetTimeout例えばNode.jsなら、「setTimeout」ではなく「setImmediate」or「nextTick」が使われます。主な目的は、重い処理する際にUIをブロックすることを避けるためです。オンにしてみると......。completed!原理は全く一緒です。処理を一旦キューに入れて、次のイベントループでキューから呼び出します。Schedulerの種類Schedulerの種類ですが、シングルスレッドのJavaScriptは今のところ以下の3つくらいです。タスクを次のイベントループで実行するようにするスケジュール(非同期処理)。その場でタスクを実行する(同期処理)。immediateSchedulerと同様、その場で実行される。再帰的な呼び出しの場合はtimeoutSchedulerの挙動になる。実例RxJSの基礎は以上です。オペーレーターの数は殺人的に多いのですが、基本的な考え方さえ押さえておけば、あとは楽です。つまらない理論はここまでにして、実例でおさらいも兼ねて情報整理しておきましょう。実例では、過度な説明を控えておりますが、オペーレーターの数は山ほどあります。「ドラッグ・アンド・ドロップ」いわゆる「ドラッグ・アンド・ドロップ」の実装ですね。});})();使ったオペーレーターflatMaptakeUntil解説flatMapを使って、「mousedownイベント」を「mouseupするまで続くmousemoveイベント」に変換しています。最近に発行した値だけを取得するBehaviorSubjectを利用してUIを更新しています。使ってBehaviorSubjectたちの値をまとめて受け取り、設定するところですね。timeoutキーとキーの間のタイムアウト(ミリ秒)、指定した時間が過ぎたら失敗と見なす。使って1回だけキャッチする。順番に連結しています。競合ライブラリ今のところ、競合と言えるライブラリは「Bacon.js」くらいでしょう。RxJSと非常に似ています。Bacon.jsをきっかけにRxJSを知りました......。使いやすさと用語の分かりやすさ共にRxJSより優れていると思います。歴史も結構あって、おすすめです。RxJSが理解できれば、すんなりと移行できると思います。下記は各ライブラリの比較です。参考にさせていただきました。いうことは、実際に開発に着手したのはもっと早い段階です(少なくとも2010年あたりからだと思います)。誠に面目ないのですが、私がまだこの業界に入っていないときから既に存在していたのにも関わらず、今更その存在を知りました......。それで、なぜ今までRxJSの存在を知らなかったのか、どうしても腑に落ちなかったので、その原因を自分なりに考えてみました。情報が少なかった何よりも情報が少なかったです。書籍はともかく、今ググっても情報はそんなに出てきません。.NETで開発している人からしたら、とっくに常識になっているのかもしれません。C#以外の言語ではどうも情報が少ないように思います。下記は関数型プログラミング支援のライブラリである「underscore.js」との月間検索ボリュームの比較です。して検索ボリュームが少ないです。Programmingをサポートするライブラリです。Programmingという概念が注目を集め始めたのはここ最近の話です。Programmingの概念自体はそんなに新しいものではないですが、恐らく「馴染みがない+その時代でのニーズがない」ことが原因で、長らく水面下で進行していました。最近になって、やっと日の目を見ることができた感じですかね。Extensions」の検索ボリュームを調べた結果です。Programmingというキーワードはほぼ同時期に関心が高まった背景事情が見て取れると思います。Programmingが引き金となって、2012年頃から現在までRxJSが伸び続けています。学習コストがかかる上記の2点から「認知度が低い」ことは容易に想像できます。そのため「学習コストがかかる」という結論が自然に導き出されます。学習コストもかかるし、実際に役に立つのかどうかも不明瞭......。こんなシチュエーションに遭遇したら、大抵の人は勉強するのをやめるでしょう。近年はWebでもリッチなアプリケーション・サービスが求められるようになってきました。Webと言えばJavaScript、JavaScriptと言えばイベント駆動、非同期処理!そう、実にRxJSと相性がいいのです。「リアクティブなアプリケーション」は1つの傾向として、今後徐々にWeb開発業界のビッグキーワードになっていくでしょう。そんな中で、RxJSおよび類似ライブラリもこの流れに乗っかって、少しずつ人目に触れるようになるのではと予測しています。まとめ今回の記事で「入門」と題したのは、私自身もRxJS初心者だからです(触り始めたのは数週間前です)。Programmingについてあれこれ調べていたのがきっかけでした。私は結構大きな衝撃を受けたので、もっとたくさんの人に知っていただきたく、RxJSの普及を願って今回この記事を書きました。Category(カテゴリ別オペレーター一覧、図解と例が付いていいてとても分かりやすいです。説明も公式レポジトリより詳細です。RxJS(基礎から応用まで、しかもインタラクティブに学習できる。Programmingの何たるかが分かりやすくコンパクトにまとまっています。POSTLIGへの自由なメッセージお待ちしています頂いたご意見への回答は行っておりません。