Deferred チェイン。
MochiKit の実装はもっとシンプルにできそうなのでつくってみた。でもいろいろ頭悪くて考えきれてないと思う……
例えばこういう風に書けるようにする。
wait(1).
next(function (e) {
log([1, e]);
return wait(1).next(function (e) {
log([2, e]);
return wait(2).next(function (e) {
log([3, e]);
});
});
}).
next(function (e) {
log(4);
return "555";
}).
next(function (e) {
log(e);
})末尾再帰っぽいの (return call ってのが tailcall っぽくてよくないですか><)
next(function () {
log("start");
}).
next(function () {
function pow (x, n) {
function _pow (n, r) {
if (n == 0) return r;
return call(_pow, n - 1, x * r);
}
return call(_pow, n, 1);
}
return call(pow, 2, 10);
}).
next(function (r) {
log([r, "end"]);
}).
error(function (e) {
alert(e);
})実装
function Deferred () { this.init.apply(this) }
Deferred.prototype = {
init : function () {
this.callback = {
ok: function (x) { return x },
ng: function (x) { throw x }
};
this._next = null;
},
next : function (fun) { return this._post("ok", fun); },
error : function (fun) { return this._post("ng", fun); },
call : function (val) { return this._fire("ok", val); },
fail : function (err) { return this._fire("ng", err); },
cancel : function () {
this._next = null;
},
_post : function (okng, fun) {
this.callback[okng] = fun;
this._next = new Deferred();
return this._next;
},
_fire : function (okng, value) {
var self = this;
var next = "ok";
try {
value = self.callback[okng].call(self, value);
} catch (e) {
next = "ng";
value = e;
}
if (value instanceof Deferred) {
value._next = self._next;
} else {
setTimeout(function () {
if (self._next) self._next._fire(next, value);
}, 0);
}
}
};
function wait(n) {
var d = new Deferred();
var t = new Date();
setTimeout(function () {
// 実際にかかった時間をコールバック
d.call((new Date).getTime() - t.getTime());
}, n * 1000)
return d;
}
function ps () {
var d = new Deferred();
setTimeout(function () { d.call() }, 0);
return d;
}
function next (fun) {
return ps().next(fun);
}
function call (f, args) {
args = Array.prototype.slice.call(arguments);
f = args.shift();
return next(function () {
return f.apply(null, args);
});
}なかなかカッコいい感じがする。もうちょいいじる。
next は this ではなくて、次の Deferred (this が準備できたらよばれる Deferred // _next) をかえす。これでずっとチェインしていく。もしコールバックが Deferred をかえしたら、その Deferred に _next をわたしてあげる。_next は継続 (ていっていいのかな。継続をちゃんと理解していない)
スタック消耗してたのを修正した