PromiseとDeferredを表面的に実装してみた。
promiseにはpromise/A という小さなルールがあって、それに則って皆ライブラリを作っているみたいだけど、それはいいからとにかくthen したくなった。
主に、promiseオブジェクトのありかたが本家を無視しています。promiseオブジェクトは、thenを一度しか呼べないルールなんじゃないか。で、thenしたら 新たなpromiseオブジェクトをnew してreturn するんじゃないかと思ってる(全部詳細未確認)。
TODO: あとでちゃんと kriskowal/q のソースよむ。
var util = require('util'); /** * @constructor */ var Promise = function(startValue) { this.startValue = startValue; this.chain = []; }; // thenすると、ステップを蓄積していく。 Promise.prototype.then = function(f) { this.chain.push(f); return this; }; // doneすると、最後のステップを挿入したあと、実行を開始する。 Promise.prototype.done = function(f) { this.then(f).call_(this.startValue); }; /** * @protected */ Promise.prototype.call_ = function(val) { var me = this; // nextTickから実行開始。 process.nextTick(function() { var rv = me.chain.shift()(val); if (me.chain.length != 0) { // 蓄積されたステップを、nextTick ごとにcallしていく。 me.call_(rv); } }); }; (new Promise).then(function() { return 'a'; }).then(function(a) { return a + 'b'; }).then(function(b) { return b + 'c'; }).done(function(out) { console.log(out); // 'abc' }); /** * @constructor * @extends {Promise} */ var Deferred = function() { Promise.call(this); }; util.inherits(Deferred, Promise); /** * @protected */ Deferred.prototype.call_ = function() { var me = this; var args = Array.prototype.slice.call(arguments); // 次の関数を呼ぶための準備をする。doneなら無視。 if (me.chain.length >= 2) { args.unshift(function next() { if (me.chain.length > 0) { // next が呼ばれたら、次のステップに移れるように。 me.call_.apply(me, arguments); } }); } // nextTickから実行開始。 process.nextTick(function() { // 蓄積した最初の関数を実行。 me.chain.shift().apply(null, args); }); }; (new Deferred).then(function(next) { setTimeout(function() { next('a'); }, 800); }).then(function(next, a) { util.print(a); setTimeout(function() { next('--', 'b'); }, 800); }).then(function(next, and, b) { util.print(and + b); setTimeout(function() { next('c'); }, 800); }).done(function(c) { console.log(c); }); // 'a', '--b', 'c' と非同期的に出力される