Promise風のものをHaskellで学んだ継続モナドを生かしてつくってみたかった。
経緯
Promiseの実装が気になってpolyfillのコードをよみました。
ざっくり読んだだけですが、
callbackの実行状態をステータス管理して、
処理が終わるまでsetTimeoutとかを使って待つような作りになっていて
以外に美しくない実装なのだなぁと思ってました。
そこで継続の概念をつかって、もっとシンプルにPromise風のものが作れないかやってみました。
目指すのは、callbackのメソッドチェーン化です。
定義
class Cont{
constructor(fn){
this.runCont = k => fn(x => k(x));
}
then(fn){
const run = this.runCont;
this.runCont = k => run(a => {
fn(a,x => k(x));
});
return this;
}
}
実行
new Cont((resolve) => {
console.log("constructor:before");
resolve(7);
console.log("constructor:after");
}).then((a,resolve) => {
console.log("then1:before");
console.log("a = " + a);
setTimeout(function() {
console.log("resolve1:before");
resolve(a+1);
console.log("resolve1:after");
}, 300);
console.log("then1:after");
}).then((a,resolve) => {
console.log("then2:before");
console.log("a = " + a);
setTimeout(function() {
console.log("resolve2:before");
resolve(a+1);
console.log("resolve2:after");
}, 200);
console.log("then2:after");
}).then((a,resolve) => {
console.log("then3:before");
console.log("a = " + a);
setTimeout(function() {
console.log("resolve3:before");
resolve(a+1);
console.log("resolve3:after");
}, 100);
console.log("then3:after");
}).runCont(a => {
console.log("runCont:"+a);
});
resolveが次のthenの関数を実行してくれます。
結果
constructor:before
then1:before
a = 7
then1:after
constructor:after
resolve1:before
then2:before
a = 8
then2:after
resolve1:after
resolve2:before
then3:before
a = 9
then3:after
resolve2:after
resolve3:before
runCont:10
resolve3:after
思ったとおりの順番で実行できてます。
うまくcallbackをメソッドチェーンで記述できているような気がします。
感想
やりたいことがシンプルに実装できたと思います。
しかし、エラー処理とか、メモリリークとか、処理速度とか、使いやすさとか、
色々考えていくとこの実装では限界があるでしょうか?
そもそもPromiseがよくわかっていない気もしてきました。
今回の実装をもとに、もうちょっと作り込んで問題点を探っていきたいと思います。
いろいろご指摘いただけるとありがたいです。