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

こんにちはー。

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

Rx.js とは?

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

コードの概要

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

  1. 3つの画像が配列になっている
  2. それらを非同期で読むが、1つ読み終わったら次を読むという条件。
  3. 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

参考

「RxJS」初心者入門 - JavaScriptの非同期処理の常識を変えるライブラリ | 株式会社LIG
こんにちは、王です。 みなさん、「RxJS」をご存知でしょうか? すごく大雑把に言うと、RxJSとは非同期処理(マウスクリックなどのイベントベースの処理も含める)をす...

追記

2016-04-07 : Rx.js を使ったメリットを中心とするように記事の内容を全面改訂。

Gulpでtsify+watchify

Qiitaにもかきました。

Gulpでtsify+watchify - Qiita
TypeScriptでBrowserifyが使える、 (https://www.npmjs.com/package/tsify)を知ったのですが、 ビルドの時間が遅いし、毎回「gulp XXX」って打つのが大変な...

TypeScriptでBrowserifyが使える、 tsifyを知ったのですが、

ビルドの時間が遅いし、毎回「gulp XXX」って打つのが大変なので、

watchifyと一緒に使えないかな・・・と思いました。

次のようなgulpfile.jsを作りました。

  • gulp watchify-tsifyと実行する。
  • ./ts/a.ts./ts/b.tsを監視する。
  • watchifyでupdateが発生するたびに、runBundle()を呼び出す。
  • b.pluginでtsifyを読み込み、js/all.jsを出力する。

参考

Gulp + browserify + watchify + gulp-sass で自動高速コンパイル環境
Gulp とはGulp は Web 制作に関するいろいろな作業を自動化するためのツールです。Sass のコンパイルや Gulp の使用には node.js や Sass などのインストールが必要なの...