2013年7月8日月曜日

JavaScriptでライブラリを使わずに同期処理っぽいものを実現する一つの方法

JavaScriptでよく困るのは、簡単に同期処理ができないことだ。

例えば、非同期処理でファイル処理などの重たい処理を大量にやってしまうと、全てのファイルを一気に処理しようとしてブラウザが固まることになるだろう(^^);

もちろん皆もその辺はよく分かっていて、同期処理っぽいものを実現するライブラリがある。

JSDeferred - Asynchronous library in JavaScript. Standalone and Compact

すばらしいライブラリなんだけど、基本的にこんな感じの数珠つなぎ型のコーディングスタイルになるようだ。

var results = [];
next(function () {
  return http.get("/foo.json").next(function (data) {
    results.push(data);
  });
}).
next(function () {
  return http.get("/baz.json").next(function (data) {
    results.push(data);
  });
}).
next(function () {
  return http.get("/baz.json").next(function (data) {
    results.push(data);
  });
}).
next(function () {
  alert(results);
});

このスタイル、あまり好きくない(^^); そこで少し目先を変えることにした。

特にファイル処理の場合、ブラウザからファイルオブジェクトの配列が渡されるので、それぞれのファイルオブジェクトに処理済みかどうかのフラグを持たせて、定期的にチェックしてはどうか?と。



実行してみると、ファイル処理のキューは一気に追加されているものの、それぞれのファイル処理は順番に行われていることが分かる。これが同期処理か?というと違うと思うけれど。

キモはprocFile()のここ。

var filePrev = files[idx - 1];
var timer;
var timerFunc = function() {
  addLine("一つ前のファイルは処理が終わってる?: name=" + filePrev.name + " isDone=" + filePrev.isDone);
  if (filePrev.isDone) {
    clearInterval(timer); //タイマー解除
    addLine("ファイル処理開始 name=" + file.name);
    file.proc(function() {
      file.isDone = true;
      addLine("ファイル処理完了 name=" + file.name);
    });
  }
};
timer = setInterval(timerFunc, 500);

つまり、setInterval()を使ってそれぞれのファイルの一つ前のファイルの処理が終わっているかどうかを定期的に見に行くという方法だ。

fileのプロパティーにerrorとかcancelとかを追加すれば、そういったケースにも対応できる。

しかし、どうなんだろうなあ・・・やっぱり数珠つなぎの方が汎用性は高そうだし、その方がいいのかなあ・・・