React の登場と共に web フロントエンド界隈でも注目を集めるようになったリアクティブ・プログラミング ( RP ) 。その中でひときわ存在感を放っている1)オレ調べ。 RxJS について学んでみようと思います。
RxJS は Reactive Extensions for JavaScript の略称です。Observables というアーキテクチャを用いたリアクティブ・プログラミング用のライブラリであり、非同期処理 ( クリックなどイベント駆動の処理も含まれます ) を簡潔かつ可読性高くコーディング出来ることを主な目的としています。
元々は Microsoft が C# 向けのライブラリとして設計・開発したものです。2009年に始動したプロジェクトから生まれたこの革新的なアーキテクチャはやがて様々な言語に移植されました。RxJS もそのうちの一つです2)他にも Java、Swift、Scala、C++、Ruby、Python、Groovy、JRuby、Kotlin などに移植されています。。
よく関数型プログラミングと混同されがちですが、それは Functional Reactive Programing ( FRP ) と呼ばれるものであり、今回の題材である Rx とは別モノです。このあたりを語るとなるとそれだけで本が一冊書けてしまうほどのボリュームになるうえ、関数型界隈のおっかない有識者の方々からマサカリが飛んでくること請け合いなので、ここでは割愛します。以下のブログエントリがそのあたりについて非常に詳しく解説されているので、ご興味がある方は是非ご一読ください。
フルスタックフレームワークである Angular は幾つかの外部ライブラリに依存しており、そのうちの一つがこの RxJS です。例として http リクエスト周りにおいて内部で Observable を返すなどといった使われ方をしています。
Cycle.js は公式では xstream というライブラリに依存した使い方が推奨されていますが、代わりに RxJS を使うことも可能です。xstream は RxJS の軽量版といった位置づけで、API の数が少なく命名にも若干変更が入れられたライブラリです。jQuery に対する Zept のようなものだと思っていただければ OK です。
Cycle.js についてもいずれこのブログにてご紹介できればと思います。
このように RxJS はそれ単体での利用だけでなく、他ライブラリの機能を補う目的でも活用されているのが特徴です。
RxJS の公式リポジトリにある README には次のように書かれています。
RxJS = Observables + Operators + Schedulers
Observables |
非同期データソース ( ストリーム ) を表すクラスで、言葉通り Observe ( 観察 ) する何か |
---|---|
Operators |
Observable に対してフィルターをかけるなど値を加工するためのメソッド群 |
Schedulers |
遅延操作をするなど Observables の並行性を制御するもの |
Rx は Observer パターンを基本に据えています。その上でイベントやデータが時間軸という名のストリームを流れる要素として扱えるように設計されたものとなっています。
もう少し具体的な例を挙げてみます。『時間軸という名のストリーム』を一本の川に例えてみましょう。その上流からユーザーはボタンクリックやテキスト入力といったアクションによって『桃』を流します。この桃こそが Observable です。流れる桃は一見するとどれも同じ見た目ですが、それぞれ中身が異なります。この中身こそがユーザーアクションによって送信された『データ』です。そしてその川に対して filter
( ある条件に一致する桃だけを下流に流す ) や merge
( 複数の川を合流させて一本の川にする ) といった操作を Operators と呼びます。
Schedulers は川の流れを遅らせたりするといったものですが、Observables や Operators ほど利用頻度は高くありません。より高度な使い方をするときに改めて学べは良いでしょう。
と、ここまで話が抽象的すぎて何のことだかサッパリかもしれませんが、はじめのうちは「川!?桃!?そういうのもあるのか!?」といった程度に読み進めていただいて構いません。自分でコードを書き進めていけばそのうち自然と理解できますので、今は騙されたと思ってスルーしてくださいませ。
長々とした能書きはこれくらいにして、そろそろ実際にコードを書いてみるとします。
サンプルコードは全て TypeScript で記述します。というのも Observables は非同期にデータが流れてくるという特性から、どういったデータが来るのかが予め明確になっていないと後々で流れの追いにくいコードになりやすいため、少しでもデータに関する情報を明確にするために型を指定できる TypeScript が適しているのです3)僕は試していませんが、もちろん FlowType でも同様の恩恵を受けられると思います。。
まずは RxJS を使わない普通の書き方です。
const button = document.querySelector('button'); button.addEventListener('click', (event: MouseEvent) => { console.log(`${(event.target as HTMLButtonElement).textContent} is clicked!!`); });
なんてことはない、ボタンをクリックするとコンソールにメッセージを出力するだけの処理です。これを RxJS を使って Observable を生成する方法に書き換えてみます。
const button = document.querySelector('button'); Rx.Observable .fromEvent(button, 'click') .subscribe( (event: MouseEvent) => { console.log(`${(event.target as HTMLButtonElement).textContent} is clicked!!`)); } );
順番に見ていきます。Observable ( ストリーム ) から fromEvent
オペレータを使ってボタンのクリックイベントを取得し流せる仕組みを作ります。次にこの流れてくるイベントデータを subscribe
( 購読 ) メソッドを使って取り出しています。
こちらのデモで実際の動きをご覧いただけます。
See the Pen My first RxJS by wakamsha (@wakamsha) on CodePen.
すでに何となくお分かりではないでしょうか。fromEvent は click
や input
などといったイベントを操作するためのオペレータです。第一引数に DOM 要素、第二引数にイベント名を指定します。第一引数は any 型となっており、DOMElement
だけでなくNodeList
, jQuery element
, Angular element
など多種多様な型に対応しています。
Observable はただ書いただけではデータを流しません。下流で subscribe ( 購読 ) することで初めてデータが流れます4)Hot と Cold の概念につきましては他エントリにて改めて解説します。。subscribe メソッドは引数に Observer
クラスを受け取りますが、以下のように3つの関数を直接受け取る書き方も可能です。Observer クラスのインスタンスは以下の3つの関数を内包したものなので、結局はどちらも同じというわけです。
subscribe( (x) => console.log(x), // onNext (error: Error) => console.error(error), // onError () => console.log('Completed!!') // onComplete )
第一引数のonNext
は新しいデータが流れてきたときに、第二引数のonError
はストリームの途中でエラーが発生したときに、第三引数のonComplete
は全てのデータが流れきってストリームが終了した時にそれぞれ呼び出されます。
RxJS を学ぶのは決して簡単ではありません。個人差はありますが、少なくともこれまでオブジェクト志向な世界に身をおいていた人にとっては『非常に難しい』部類に入るかと思います。その理由は膨大な API の数に加えて命令形から宣言型への根本的なコンセプトの変更があるからです。当ブログでは膨大な RxJS の世界から比較的よく使われるであろう機能やちょっとした TIPS などをかいつまんでご紹介できればと考えております。
今回は導入編ということで RxJS の簡単な概要とさわりだけをご紹介しました。次回からは RxJS の様々な機能をデモとともにご紹介するなど本格的なリアクティブ・プログラミングの世界に入っていきたいと思います。
なんだかんだ公式ドキュメントが一番です。チュートリアルもそこそこ充実しているので、まずは🤞を熟読するのをオススメします。ググれば日本語の情報もある程度ヒットしますが、その多くがバージョン 4.x
の頃に書かれたものだったりします。そのため現行の 5.x
だと動作しないコードが結構あるので注意が必要です。
脚注
1. | ↑ | オレ調べ。 |
2. | ↑ | 他にも Java、Swift、Scala、C++、Ruby、Python、Groovy、JRuby、Kotlin などに移植されています。 |
3. | ↑ | 僕は試していませんが、もちろん FlowType でも同様の恩恵を受けられると思います。 |
4. | ↑ | Hot と Cold の概念につきましては他エントリにて改めて解説します。 |
リクルートマーケティングパートナーズではたらく Web フロントエンド・エンジニア。サプリシリーズをはじめとした内製サービスの開発メンバーとして、仕様策定や技術選定にも携わる。ブログなどで積極的にWeb技術の情報発信に取り組んでいるが、実はプログラミングよりも洋服を自作する方が得意だったりする。
この執筆者の記事一覧
※ コメントはこちらのに同意の上、投稿ください。