LoginSignup
6
2

More than 3 years have passed since last update.

単純化したPromiseを作ってみた

Posted at

単純化したPromiseを作ったらPromiseが分かると思って作ってみた。

単純化したPromiseのプログラム1

JavaScript
//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が分かりにくい理由

JavaScript
var p = new Promise((resolve) => {
    // 処理
    setTimeout(() => { resolve('a'); }, 3000);
};

(resolve)=>{}setTimeoutと似ており、new Promiseの引数関数がTask Queueに追加されると思うかもしれないが、実は違う。

単純化したPromiseを作ってみて分かったことは、new Promiseの引数関数は即時実行される。

このプログラムは以下のプログラムにリファクタリングできる。

JavaScript
// 処理 (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がある場合が分かる。

JavaScript
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に追加する。

JavaScript
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は引数関数を渡す形式になっている。


  1. Promiseの完全版はjsdelivr.netなどで見れる。Promiseの仕様はpromises-specでCC0のパブリックドメインで公開されている。 

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2