JavaScript
FRP
RxJS
Bacon.js
Kefir.js

ECMAScript7を見据えた、JavaScript(TypeScript)で使えるFRPライブラリの比較調査

More than 3 years have passed since last update.


動機

Reactive Streamsという非同期ストリーム処理標準化の流れがきているし、ECMAScript7でObservableが入りそう

同期
非同期

単数データ
(Arrayを含まない)Object
{}, 1, "foo", var y = f(x)

Promise
Promise.then(callback).then(callback)

複数データ
Array(ES6でIteratorが入りそう
[1, 2].filter().map()

Stream/Observable
Observable.filter.map.subscribe(callback)

(図はFunctional Reactive Programming with RxJSを参考にした。)

Observableは非同期処理に超絶便利なので、JavaScriptでも今から似たようなものを使いたい。

ただ、将来的にES7の標準に乗り換えたいので、それを軽く見据えて調査する。


比較表

名称
安定性
古いブラウザ互換性
ライブラリサイズ
パフォーマンス
多言語
情報量
JQueryとの協調
TypeScript用型定義

RxJS
安心
あり
大(RxJS-Modularというモジュール版ができはじめたので、それを使えば必要最低限で済みそう)
普通
O
かなり多い
O
公式である

Bacon.js
安心
あり

重い
X
多い
O
ある

Kefir.js
不安
明示されていない

軽い
X
少ない
O
ない


各ライブラリの特徴


RxJS

RxJSはC#発祥のReactive Extensionsのjs移植。基本的に他言語でも統一された仕様に従っている。

RxJS = Observables + Operators + Scheduler ということになっている。

ObservableにはHotとColdという注意すべき特性があり、とくに型で区別している訳でもなく、メソッドで判別しなければいけないので難しい。

Rxからほとんどのメソッドを導入しているので、メソッドが非常に多いが、機能の少ないlite版もある。


Bacon.js

Bacon.jsはRxJSが非オープンソースでドキュメントが少ない時代に作られ始めたらしい。(reactive-baconが前身っぽい)

設計思想的にはRxJSより一貫性があるとされる。

RxJSのHot, Coldという特性の違いが気に入らず、Coldを廃したような挙動にしている。

EventStreamとPropertyという2種類のストリームに分けているが、Observableのインターフェースを持ち、共通のメソッドも多い。

また、RxJSと違ってデフォルトではエラーイベントでストリームを終わらせない。

参考:

- https://github.com/baconjs/bacon.js/#why-bacon

- https://github.com/baconjs/bacon.js/#for-rxjs-users


Kefir.js

Kefir.jsはBacon.jsとRxJSの影響を受けつつ、速度と省メモリを主目的にしている。

Bacon.jsのようにObservableをStreamとPropertyという2種類に分けている。

Baconもそうだが、Streamは離散的に、Propertyは連続的に値を持つので、Subscribeで渡したコールバックが実行されるタイミングが違う。


安定性

3つのライブラリはどれもテストが豊富に書かれている。

RxJSとBacon.jsはissueが同じくらい挙がっていて順次処理されている。

RxJSは、オープンソース化する前も含めると少なくとも2010年から開発されている。

Bacon.jsは、前身のReactive-Baconが2012年から開発されている。

Kefir.jsは、2014年から開発されているが、1系がリリースしたばかりでまだ枯れていない感じがある。


古いブラウザ互換性


ライブラリサイズ

で確認。

Rxについては、モジュール版のRxJS-Modularというのが作られ始めたので、それを使うと少なくて済みそう。


パフォーマンス

RxJS, Bacon.js, Kefir.jsのベンチマークでメモリ使用量について言及されており、

Kefir.js、RxJS、Bacon.js、の順に悪くなるとされている。

Kefir.js, Bacon.jsのベンチマークでは、目に見えてKefir.jsのパフォーマンスが良い。

ただ、よほどリアルタイム性や省メモリが重視される用途でなければ、どれを選んでも問題なさそう。

比較的重いとされるBacon.jsでも、特に問題は聞いていないと書いてある。

ちなみにRxJS 2.3.25からベンチマークが追加されていた。


他言語対応

RxJSは、Rxがたくさんの言語で実装されているので、学習すると他言語で知見を活かせる。

とはいえ、BaconもKefirもRxJSと似通っているので、それを選んだから知見が無駄になるということもない。


情報量

RxJSはRx自体の記事も多いので、文献は豊富。APIの図解もReactive Extensionsのものを見れば載っている。

Bacon.jsも記事はそれなりにある。

Kefir.jsは最近1系になったばかりで、まだ全然ない。


JQueryとの協調

RxJSにはfromEventとfromPromise、Bacon.jsにはasEventStreamとfromPromiseがあり、JQueryのイベントやPromiseをObservableに変換できる。

Kefir.jsにはJQueryPluginがある。


TypeScript用型定義

やっぱり型い言語で書きたいので、調べた。

RxJSには公式の型定義ファイルがあるのでTypeScriptですぐに使える。ただ、若干間違っている部分があったりする。

Bacon.jsにはあるが、追従しているかは調べていない。

Kefir.jsには型定義ファイルがない。


どれが一番ES7に乗り換えやすそうか?

まだasync generatorStrawman段階なので仕様も変わる可能性が大いにあり、何とも言えない。

だが、ES7のObservableは既存のライブラリを参考にしていることもあり、合成できるよう設計されているし、どれを選んでも大きく逸脱しないだろう。

現状、Observableの性質の分類に対する思想がライブラリ間で違うが、ES7になって標準Observableに収斂してそういう分類が消えるのではなく、標準Observableのインターフェースを持ちつつ細分化したライブラリが出てくることも考えられるので、分類思想の違いが消滅して無駄になることもない、かも。


結論

現状ではES7がどうなるか不確定なので何も言えない。

ES7にObservableが入らないならこれらのライブラリに頼るしかないし、入るならこれらのライブラリを参考にすると思うので、ES7のことを意識しないで、好きなのを使っていいんじゃないかな。


  • 安定性・TypeScriptですぐ使う・他言語で知見を活かす ならRxJS

  • 安定性・一貫性のあるObservable重視 ならBacon.js

  • 軽量・性能重視 ならKefir.js