Hatena::Groupjavascript

JavaScriptで遊ぶよ

 | 

2009-04-13

JSDeferredのソース読んでる・その4

02:36

昨日まで。

重要なところはほとんど全部読んだ。あとは parallel と loop だけ。他に、JSDeferred Sample にいくつかサンプルがある。

parallel も loop も、サンプルのコードと構造は同じようなもの。昨日までに見た JSDeferred の機構の応用という意味で、オレオレ JSDeferred プラグイン (というのか?) を書くときに参考になると思う。


parallelの使いかた

これは一番わかりにくいところかも。

こんなふうに使う。

var o = {};
Deferred.define(o);
o.func = function(){print(1); return this;}

Deferred.parallel([ // 配列かハッシュのどちらかで渡す
  /* 使い方 1 : Deferred.next を定義したオレオレオブジェクトを渡す */
  o.func()
  /* 使い方 2 : Deferred チェーンを渡す */
 ,Deferred.next( function(){ print(2) } )
 ,Deferred.wait(1).next( function(){ print(3) } )
 ,Deferred.call(print, 4)
  /* 使い方 3 : 新しい Deferred インスタンスを作ってそれを渡す */
 ,(function(){
    var d = new Deferred();
    setTimeout(function(){
      print(5);
      d.call();  // ← ここで call することで遅延チェーンが繋がる
    },2000);
    return d;
  })()  
])
.next(function(){ // もちろん Deferred チェーンを繋げることができる。
  print(6);
})

/* 結果 */
// 1
// 2
// 4 ←ここまで即座
// 3 ←これだけ1秒後
// 5 ←ここから2秒後
// 6

要するに、parallel に渡された配列・ハッシュの各オブジェクトから next で Deferred チェーンが繋げることが条件。

parallel に渡すのは関数ではなくて、関数の実行結果だということに注意。


parallel の中身を見る

Deferred.parallel = function (dl) {
	var ret = new Deferred(), values = {}, num = 0;
	for (var i in dl) if (dl.hasOwnProperty(i)) (function (d, i) {
		d.next(function (v) {
			values[i] = v;
			if (--num <= 0) { // このブロックはとりあえず無視
				if (dl instanceof Array) {
					values.length = dl.length;
					values = Array.prototype.slice.call(values, 0);
				}
				ret.call(values);
			}
		}).error(function (e) {
			ret.fail(e);
		});
		num++;
	})(dl[i], i);

	if (!num) Deferred.next(function () { ret.call() });
	ret.canceller = function () { // canceller も今は完全無視
		for (var i in dl) if (dl.hasOwnProperty(i)) {
			dl[i].cancel();
		}
	};
	return ret;
};

for ループ部分で、引数の配列・ハッシュの要素ごとに next → error のチェーンを負荷していることになる。チェーンの内容は、それぞれの Deferred インスタンスの callback.ok 関数の実行結果を集約する (value というオブジェクトに入れる) というもの。

d.next(〜).error の時点では next に 入れた関数は実行されていない (チェーンが構築されただけ)。だからこの時点で num は、parallel の引数配列・ハッシュの要素数に一致する。

それぞれの next の中身が実行されていくにつれて、--num で一つずつ減っていく。もし最後まで (エラーなく) 実行されたら、「とりあえず無視」と書いたブロックが発動することになる。

この部分はわかりにくいのだけど、

if (dl instanceof Array) {
	values.length = dl.length;
	values = Array.prototype.slice.call(values, 0);
}

このブックマークレットを実行してみたら理解できると思う。

javascript:var a={0:'a', 1:'b', 2:'c', 3:'d'}; a.length=4; alert(Array.prototype.slice.call(a, 0));

任意のオブジェクト配列に変換できるんだね。a.length = 4 が無いと思った通り動いてくれない。a.length = 3 とかだったら途中で切れる。

そして、晴れて ret.call(values) によって parallel の次のチェーンが実行されるというわけ。

if (!num) Deferred.next(function () { ret.call() }); は、配列・ハッシュのサイズが零だったときにしか呼ばれない。とりあえず次のチェーンに繋げるための処理。


まとめると、parallel それ自体は新しい Deferred インスタンスを返し、内部では複数の Deferred チェーンを構築し、並列処理実行の一番最後で自身を call する、ということになる。


次回は

loop と、サンプルにある aloop を見るつもり。もともと aloop が理解したくて JSDeferred を読み始めたんだった。


追記

総括も書いたよ。

 |