この記事は bouzuya's RxJS Advent Calendar 2015 の 21 日目かつ RxJS Advent Calendar 2015 の 21 日目です。
はじめに
いまさらながら Event を Observable で操作します。Observable.fromEvent とその簡単な例を書きます。
通常なら DOM の Event で使うところですが、ここまで Node.js で試してきたので EventEmitter を使います。どちらも Observable.fromEvent で対応できます。
Observable.fromEvent
EventEmitter の例
まず Observable を使わない EventEmitter の例を挙げます。
import { EventEmitter } from 'events';
const emitter = new EventEmitter();
emitter.on('data', value => console.log(value));
emitter.emit('data', 'Hello, Event!');
// Hello, Event!
EventEmitter.prototype.on で event listener を登録します。ここでは流れてくる 'data' event の値を表示するようにしています。EventEmitter.prototype.emit で 'data' event として 'Hello, Event!' を流しています。
Observable の例
次に Observable を使った EventEmitter の例を挙げます。
import { EventEmitter } from 'events';
import { Observable } from 'rx';
const emitter = new EventEmitter();
Observable
.fromEvent(emitter, 'data') // fromEvent(element, 'cilcked')
.subscribe(value => console.log(value));
emitter.emit('data', 'Hello, Event!');
// Hello, Event!
Observable.fromEvent で emitter の 'data' event から Observable instance をつくっています。Observable.prototype.subscribe は説明不要でしょう。
自然に読みかえられると思います。
Observable の良い点
前述の Observable の例では 1 行増えただけで良い点が分かりづらいので、もうすこしいくつかの例を挙げます。
流れてくる文字のほかにそれを mask ('*' でつぶす) した文字を表示するようにします。愚直に書くとこんな感じでしょうか。
import { EventEmitter } from 'events';
import { Observable } from 'rx';
const emitter = new EventEmitter();
Observable
.fromEvent(emitter, 'data')
.subscribe(value => console.log(value));
Observable
.fromEvent(emitter, 'data')
.map(s => s.split('').map(() => '*').join(''))
.subscribe(value => console.log(value));
emitter.emit('data', 'H');
emitter.emit('data', 'He');
emitter.emit('data', 'Hell');
emitter.emit('data', 'Hello, Event!');
// H
// *
// He
// **
// Hell
// ****
// Hello, Event!
// *************
まず Observable の Operator を使えるのは良い点です。map や filter で event を変換したり不要なものを取り除いたり……この Advent Calendar で 10 日以上をかけても紹介しきれない Operator を使えます。
また interface が統一されるのも良い点です。今回は EventEmitter ですが、それが element になっても、同様に Observable として扱えるのは良い点でしょう。
ほかの良い点を示すためにもう一つ例を挙げます。
import { EventEmitter } from 'events';
import { Observable } from 'rx';
const emitter = new EventEmitter();
const event$ = Observable.fromEvent(emitter, 'data');
const masked$ = event$.map((s) => s.split('').map(() => '*').join(''));
Observable
.merge(event$, masked$)
.subscribe(value => console.log(value));
emitter.emit('data', 'H');
emitter.emit('data', 'He');
emitter.emit('data', 'Hell');
emitter.emit('data', 'Hello, Event!');
// H
// *
// He
// **
// Hell
// ****
// Hello, Event!
// *************
実行結果は同じですが先ほどより随分と重複コードを減らしています。また mask された値を流す Observable に適切な名前をつけています。このように柔軟に分割できることで個別に Observable の動きを test することもできるでしょう。
おわりに
実はまだ全体の 2/5 くらいで、続きを書いているのですが、長くなるので分割します。明日は Observable.fromEvent (Observable.fromEventPattern) の実装を読んでいこうと思います。