サイトトップ

Director Flash 書籍 業務内容 プロフィール

HTML5テクニカルノート

RxJS入門 01: RxJSを使ってみる


RxJSObservableオブジェクトを軸にしたリアクティブプログラミングの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クラスを軸に、ObserverSchedulersSubjectsといったクラスやオペレータ(関数)が提供されます。Arrayクラスの新しいメソッド(map()filter()reduce()every()など)を参考にしたやり方で、非同期のイベントを整理して処理できるのです。ライブラリのもととなっているReactiveXは、ObserverとIteratorのパターンを組み合わせ、さらに関数型プログラミングも用いて連続したイベントを扱います。

RxJSが非同期のイベントを巧みに処理するためのおもな要素はつぎのとおりです。

03 イベントを捉える

簡単なイベントの扱いからはじめましょう。<body>要素につぎのように<buttun>要素を加えておきます。

<body>要素

<button type="button">button</button>

まずは、標準のJavaScriptコードでつぎのようにイベントリスナーを使います。なお、用いる構文はECMAScript 2015 (ECMAScript 6)です。ボタンをクリックすれば、ブラウザのコンソールにテキスト(clicked!)が示されます。

標準JavaScript

const button = document.querySelector('button');
button.addEventListener('click', (event) => console.log('clicked!'));

RxJSでは、Observableインスタンスをつくるのが基本です。静的メソッドObservable.fromEvent()は、引数にターゲットとイベントを渡すと、Observableオブジェクトが返されます。イベントが起こったときのコールバックを定めるのがObservable.subscribe()です。つぎのコードは、やはりボタンクリックで、コンソールにテキスト(clicked!)を出力します。

RxJS

const button = document.querySelector('button');
Rx.Observable.fromEvent(button, 'click')
.subscribe((event) => console.log('clicked!'));

04 関数型プログラミングの手法を用いる

RxJSは関数型プログラミングの手法を採り入れています。エラーを減らし、起こった場合も見つけやすいコードが書けるのです。前項のコードに手を加えて、クリック回数が示されるようにしましょう。すると、標準のJavaScriptコードでは、つぎのように回数を納める変数(count)がなければなりません。そしてこの変数は、別のコードから書き替えることもできてしまいます。ボタンをクリックするたびに回数は1ずつ加算されますので、動きは問題ありません。

標準JavaScript

let count = 0;
const button = document.querySelector('button');
button.addEventListener('click', (event) => console.log(`count: ${++count}`));

関数型プログラミングでは、処理を関数で加えてゆくことができます。Observable.scan()は、第1引数に渡したコールバックの戻り値をつぎの引数に渡します。第2引数(0)ははじめのコールバックが受け取る初期値です。Array.reduce()メソッドと考え方は同じです。つぎのRxJSのコードで、ボタンクリックの回数(count)がカウントアップされます。そして、回数は引数で渡されるので、外からは参照できません。

RxJS

const 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)も増えます。クリックした回数のほか秒数も示されますので、処理時間に間隔が開いたことを確かめられるでしょう。

標準JavaScript

let 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()が引数のミリ秒数の間、値(イベント)の出力を止めます。このメソッドを、つぎのように処理がはじまる前に差し込んでしまえばよいのです。時間は引数に与えるだけですから、別に変数にとっておかずに済みます。

RxJS

const 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}`));

値の流れを制御するオペーレータには、ほかにもたとえばつぎのようなものがあります。

06 処理に値を加える

前項のコードに、処理する値をさらに加えます。調べるのは、ボタンクリック(clickイベント)のとき[shift]キーを押していたかどうかです。イベントのコールバックが受け取る引数から、MouseEvent.shiftKeyプロパティでブール(論理)値が得られます。そのときには、カウント数(count)を減らすことにしましょう。

標準JavaScript

let 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)として渡るのです。

RxJS

const 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入門


作成者: 野中文雄
作成日: 2018年2月17日


Copyright © 2001-2018 Fumio Nonaka.  All rights reserved.