この記事は 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
) の実装を読んでいこうと思います。