【Rx.js】画像を非同期に読み込むサンプルコード

TOC

  1. Rx.js とは?
  2. コードの概要
  3. 【Before】Rx.js を使わない場合のコード
  4. 【After】Rx.js の降臨
  5. Rx.js を使うメリット
    1. キャンセル時の処理を追加できる
    2. filter ができる
  6. 参考
  7. 追記

こんにちはー。

非同期の処理をいい感じに使える js のライブラリないかなーと探していたら、
Rx.js というのを見つけたので、それを使ったサンプル?を紹介します。

Rx.js とは?

.NET 向けの Reactive Extensions を js 用に移植したもの。
ぼくは、最初 .NET の方を使っていて、
「それの js 版があったらいいな~」と思っていたらピンポイントであったという感じですー。

コードの概要

では、今回書いてみるコードの概要を紹介します。

  1. 3つの画像が配列になっている
  2. それらを非同期で読むが、1つ読み終わったら次を読むという条件。
  3. 1つ読み終わった後と全部読み終わった後にコールバックする

全部いっぺんに非同期で読めばいいと思うかもしれませんが、
画像がウエーブみたいになって読み込まれていくようにしたかったんです。

【Before】Rx.js を使わない場合のコード

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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 の降臨

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 読み込むとき、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回、○個目を読み込みましたと表示」するようにしてみます。

1
2
3
4
5
6
7
8
9
10
// 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 を使ったメリットを中心とするように記事の内容を全面改訂。