HTML5テクニカルノート
RxJS入門 01: RxJSを使ってみる
- ID: FN1802004
- Technique: HTML5 / JavaScript
- Library: RxJS 5.5.6
RxJSはObservable
オブジェクトを軸にしたリアクティブプログラミングのJavaScriptライブラリです。非同期の処理やイベントにもとづくコードが簡単に組み立てられます。本稿では、RxJSライブラリをインストールしたうえで、公式サイト「Manual」の「Introduction」に沿っていくつか短いコードを書いて試してみます。
01 RxJSライブラリをCDNで読み込む
RxJSを手っ取り早く試すには、CDNから読み込むのが手軽でしょう。HTMLドキュメントの<head>
要素に、<script>
要素をつぎのように加えます。使うのは本稿執筆時に安定版とされているRxJS 5.5.6です。
<head>要素<script src="https://unpkg.com/rxjs/bundles/Rx.min.js"></script>
npmなどを用いたそのほかのインストールの仕方についてはGitHubのRxJS 5「Installation and Usage」または「Installation」をご覧ください。ただし、後者のCDNはリンクが切れているようです。
02 RxJSとは
RxJSは非同期処理やイベントにもとづくプログラムを、連続したobservable
オブジェクトで組み立てるライブラリです。Observable
クラスを軸に、Observer
、Schedulers
、Subjects
といったクラスやオペレータ(関数)が提供されます。Array
クラスの新しいメソッド(map()
、filter()
、reduce()
、every()
など)を参考にしたやり方で、非同期のイベントを整理して処理できるのです。ライブラリのもととなっているReactiveXは、ObserverとIteratorのパターンを組み合わせ、さらに関数型プログラミングも用いて連続したイベントを扱います。
RxJSが非同期のイベントを巧みに処理するためのおもな要素はつぎのとおりです。
Observable
: 値やイベントをまとめてあとから取り出せるObserver
: コールバックのまとまりで、Observable
から値が送られたことを捉えるSubscription
:Observable
の実行を示し、その取り消しに用いられる- オペレータ: 関数型プログラミングでデータのまとまりを処理する関数
map()
、filter()
、concat()
、flatMap()
など
Subject
:EventEmitter
で、値やイベントを複数のObserver
にマルチキャストできるScheduler
: 複数の平行処理を制御して、いつ実行すべきかを管理するsetTimeout()
やrequestAnimationFrame()
などと同じ
03 イベントを捉える
簡単なイベントの扱いからはじめましょう。<body>
要素につぎのように<buttun>
要素を加えておきます。
<body>要素<button type="button">button</button>
まずは、標準のJavaScriptコードでつぎのようにイベントリスナーを使います。なお、用いる構文はECMAScript 2015 (ECMAScript 6)です。ボタンをクリックすれば、ブラウザのコンソールにテキスト(clicked!)が示されます。
標準JavaScriptconst button = document.querySelector('button'); button.addEventListener('click', (event) => console.log('clicked!'));
RxJSでは、Observable
インスタンスをつくるのが基本です。静的メソッドObservable.fromEvent()
は、引数にターゲットとイベントを渡すと、Observable
オブジェクトが返されます。イベントが起こったときのコールバックを定めるのがObservable.subscribe()
です。つぎのコードは、やはりボタンクリックで、コンソールにテキスト(clicked!)を出力します。
RxJSconst button = document.querySelector('button'); Rx.Observable.fromEvent(button, 'click') .subscribe((event) => console.log('clicked!'));
04 関数型プログラミングの手法を用いる
RxJSは関数型プログラミングの手法を採り入れています。エラーを減らし、起こった場合も見つけやすいコードが書けるのです。前項のコードに手を加えて、クリック回数が示されるようにしましょう。すると、標準のJavaScriptコードでは、つぎのように回数を納める変数(count)がなければなりません。そしてこの変数は、別のコードから書き替えることもできてしまいます。ボタンをクリックするたびに回数は1ずつ加算されますので、動きは問題ありません。
標準JavaScriptlet count = 0; const button = document.querySelector('button'); button.addEventListener('click', (event) => console.log(`count: ${++count}`));
関数型プログラミングでは、処理を関数で加えてゆくことができます。Observable.scan()
は、第1引数に渡したコールバックの戻り値をつぎの引数に渡します。第2引数(0)ははじめのコールバックが受け取る初期値です。Array.reduce()
メソッドと考え方は同じです。つぎのRxJSのコードで、ボタンクリックの回数(count)がカウントアップされます。そして、回数は引数で渡されるので、外からは参照できません。
RxJSconst button = document.querySelector('button'); Rx.Observable.fromEvent(button, 'click') .scan((count) => count + 1, 0) .subscribe((count) => console.log(`count: ${count}`));
05 イベントの流れを操作する
RxJSのオペーレータ(関数)は、イベントの流れをObservable
によりさまざまに操作できます。前項のコードで、ボタンクリックの処理はつづけざまに受けつけず、一定の時間(1秒)を開けることにしましょう。標準のJavaScriptコードでは、つぎのように前回処理した時間を変数(lastClick)で覚えておかなければなりません。補助的な変数(rate)も増えます。クリックした回数のほか秒数も示されますので、処理時間に間隔が開いたことを確かめられるでしょう。
標準JavaScriptlet count = 0; const rate = 1000; let lastClick = Date.now() - rate; const button = document.querySelector('button'); button.addEventListener('click', (event) => { if (Date.now() - lastClick >= rate) { console.log(`count: ${++count}, sec: ${Math.floor(Date.now() / 100) % 1000 / 10}`); lastClick = Date.now(); } });
RxJSでは、Observable.throttleTime()
が引数のミリ秒数の間、値(イベント)の出力を止めます。このメソッドを、つぎのように処理がはじまる前に差し込んでしまえばよいのです。時間は引数に与えるだけですから、別に変数にとっておかずに済みます。
RxJSconst button = document.querySelector('button'); Rx.Observable.fromEvent(button, 'click') .throttleTime(1000) .scan((count) => count + 1, 0) .subscribe((count) => console.log(`count: ${count}, sec: ${Math.floor(Date.now() / 100) % 1000 / 10}`));
値の流れを制御するオペーレータには、ほかにもたとえばつぎのようなものがあります。
Observable.filter()
Observable.delay()
Observable.debounceTime()
Observable.take()
Observable.takeUntil()
Observable.distinct()
Observable.distinctUntilChanged()
06 処理に値を加える
前項のコードに、処理する値をさらに加えます。調べるのは、ボタンクリック(click
イベント)のとき[shift]キーを押していたかどうかです。イベントのコールバックが受け取る引数から、MouseEvent.shiftKey
プロパティでブール(論理)値が得られます。そのときには、カウント数(count)を減らすことにしましょう。
標準JavaScriptlet count = 0; const rate = 1000; let lastClick = Date.now() - rate; const button = document.querySelector('button'); button.addEventListener('click', (event) => { if (Date.now() - lastClick >= rate) { count += event.shiftKey ? -1 : 1; console.log(`count: ${count}, sec: ${Math.floor(Date.now() / 100) % 1000 / 10}`); lastClick = Date.now(); } });
RxJSのObservable.map()
は、引数の関数が返す値をObservable
で出力します。以下のように、戻り値がつぎのオペレータ(Observable.scan()
)に引数(shiftKey)として渡るのです。
RxJSconst button = document.querySelector('button'); Rx.Observable.fromEvent(button, 'click') .throttleTime(1000) .map(event => event.shiftKey) .scan((count, shiftKey) => count + (shiftKey ? -1 : 1), 0) .subscribe((count) => console.log(`count: ${count}, sec: ${Math.floor(Date.now() / 100) % 1000 / 10}`));
サンプル001■RxJS + ES6: Using RxJS
値をつくって加えるオペーレータには、ほかにもたとえばつぎのようなものがあります。
RxJS入門
- RxJS入門 02: Observable
- RxJS入門 03: Observer
- RxJS入門 04: Subscription
- RxJS入門 05: Subject
- RxJS入門 06: オペレータ
- RxJS入門 07: Scheduler
作成者: 野中文雄
作成日: 2018年2月17日
Copyright © 2001-2018 Fumio Nonaka. All rights reserved.