単純化したPromiseを作ったらPromiseが分かると思って作ってみた。
単純化したPromiseのプログラム1
//CC0 license
class myPromise {
constructor(executor) {
executor((value) => {
this.do_resolve(value);
});
}
do_resolve(value) {
//setTimeoutを使ってthenの引数関数をTask Queueに追加する。
setTimeout(() => {
this.callback(value);
}, 0);
}
then(callback) {
this.callback = callback;
}
}
var v_promise = new myPromise((resolve) => {
console.log('in Promise');
//resolveをsetTimeoutの引数関数内で呼ぶ。
setTimeout(() => {
resolve('a');
}, 3000);
});
v_promise.then((value) => {
console.log('in then, value =', value);
});
console.log('last line');
プログラムの出力
in Promise // new myPromiseの引数関数は即時実行される。
last line
in then, value = a // 最後の行まで実行してからTask Queueのタスクを実行する。
Task Queueとは?
イベントループとは一体何ですか?の動画で説明される。
setTimeoutやonClickで起動された非同期タスクはTask Queueに追加される。
タスクがなくなったらTask Queueからタスクを取り出して実行する。
これはシングルスレッドのJavaScriptが非同期的に並行処理する仕組み。
Promiseが分かりにくい理由
var p = new Promise((resolve) => {
// 処理
setTimeout(() => { resolve('a'); }, 3000);
};
(resolve)=>{}
はsetTimeout
と似ており、new Promiseの引数関数がTask Queueに追加されると思うかもしれないが、実は違う。
単純化したPromiseを作ってみて分かったことは、new Promiseの引数関数は即時実行される。
このプログラムは以下のプログラムにリファクタリングできる。
// 処理 (Promiseの外に持っていく)
var p = new Promise((resolve) => {
setTimeout(() => { resolve('a'); }, 3000);
};
ではnew Promise( (resolve)=>{} )
はなぜ引数関数を渡す形式になっているのだろうか?
new Promise( (resolve)=>{resolve('a')} )
の引数関数(resolve)=>{resolve('a')}
は、( (resolve)=>{resolve('a')} ) ( (value)=>{do_resolve(value)} )
として実行され、resolve('a')
を実行したときdo_resolve('a')
を実行するようになっている。
new Promise( (resolve) => {} )
が引数関数を渡す形式になっているのは、resolve('a')
を実行したときdo_resolve('a')
を実行させるため。resolve
はプログラマーが任意の名前を付けられる。
ではdo_resolve
は何をするのだろうか?
do_resolve
はthenの引数関数をTask Queueに追加する。
2つのthenがある場合
PromiseとTask Queueの関係が分かったら、2つのthenがある場合が分かる。
var v_promise = new Promise((resolve) => { resolve('b'); });
v_promise.then((value) => {
console.log("then 1 value =", value);
});
v_promise.then((value) => {
console.log("then 2 value =", value);
});
このプログラムは1回のresolveが2つのタスクをTask Queueに追加する。
var v_promise = new Promise((resolve) => { resolve('c'); });
v_promise.then((value) => {
console.log("then value =", value);
return 'c2';
}).then((value) => {
console.log("then value =", value);
});
thenがチェーンでつながる場合は1回のresolveが1つのタスクをTask Queueに追加する。
まとめ
単純化したPromiseを作ってみたら以下のことが分かった。
setTimeoutと似ているから、new Promiseの引数関数がTask Queueに追加されると思うかもしれないが実は違う。
new Promiseの引数関数は即時実行される。
Task Queueに追加されるのはthenの引数関数。
プログラマーが名前をつけたresolveで、resolve('a')を実行したときdo_resolve('a')を実行させるためnew Promiseは引数関数を渡す形式になっている。
-
Promiseの完全版はjsdelivr.netなどで見れる。Promiseの仕様はpromises-specでCC0のパブリックドメインで公開されている。 ↩