こんにちはー。
非同期の処理をいい感じに使える js のライブラリないかなーと探していたら、
Rx.js というのを見つけたので、それを使ったサンプル?を紹介します。
Rx.js とは?
.NET 向けの Reactive Extensions を js 用に移植したもの。
ぼくは、最初 .NET の方を使っていて、
「それの js 版があったらいいな~」と思っていたらピンポイントであったという感じですー。
コードの概要
では、今回書いてみるコードの概要を紹介します。
- 3つの画像が配列になっている
- それらを非同期で読むが、1つ読み終わったら次を読むという条件。
- 1つ読み終わった後と全部読み終わった後にコールバックする
全部いっぺんに非同期で読めばいいと思うかもしれませんが、
画像がウエーブみたいになって読み込まれていくようにしたかったんです。
【Before】Rx.js を使わない場合のコード
var images = ["1.png", "2.png", "3.png"];
function loadImages(oneByOne, finished) {
var index = 0;
var loadImage = (name) => {
var elem = document.createElement("image");
elem.src = name;
elem.onload = function() {
oneByOne(index);
if (index >= images.length - 1) {
finished();
} else {
loadImage(images[++index]);
}
}
};
loadImage(images[index]);
}
function main() {
loadImages((index) => {
console.log(index + "個目を読み込みました");
}, () => {
console.log("読み込み完了");
});
}
このコードを、Rx.js を使って、シンプルにしてみます。
【After】Rx.js の降臨
// 読み込むとき、ES6 ( + browserify) だと便利
import Rx from "rx"; // ES6 modules
var images = ["1.png", "2.png", "3.png"];
function loadImages() {
return Rx.Observable.create(loadImagesObservable);
}
function loadImagesObservable(observer) {
var index = 0;
var loadImage = (name) => {
var elem = document.createElement("image");
elem.src = name;
elem.onload = function() {
observer.onNext(index);
if (index >= images.length - 1) {
observer.onCompleted();
} else {
loadImage(images[++index]);
}
}
};
loadImage(images[index]);
return function() { };
}
function main() {
var subscription = loadImages().subscribe((index) => {
console.log(index + "個目を読み込みました");
}, err => {
console.log("Observe Error: " + err);
}, () => {
console.log("読み込み完了");
});
}
- 1つ読み終わった時 ->
observer.onNext
- 全部読み終わった時 ->
observer.onCompleted
に当てはめました。
Rx.js を使うメリット
キャンセル時の処理を追加できる
上のコードでは書いていませんが、
subscription.dispose
でキャンセルでき、
loadImagesObservable の返り値の関数で、dispose された時の処理を追加できるので、
そこで読み込みの処理を停止するように書けば、簡単にできます!
キャンセル時の処理を書いた例: Gist
filter ができる
loadImages().subscribe(...)
を、loadImages().filter(...).subscribe(...)
にすれば、
フィルターを通すことも簡単にできます。
フィルターを使った例として、「5回に1回、○個目を読み込みましたと表示」するようにしてみます。
// function main() 内を変更
function main() {
var subscription = loadImages().filter(index => index % 5 === 0).subscribe((index) => {
console.log(index + "個目を読み込みました");
}, err => {
console.log("Observe Error: " + err);
}, () => {
console.log("読み込み完了");
});
}
フィルターを利用した例の全コード: Gist
参考
追記
2016-04-07 : Rx.js を使ったメリットを中心とするように記事の内容を全面改訂。