Help us understand the problem. What is going on with this article?

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

単純化した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のパブリックドメインで公開されている。 

querykuma
一番好きな関数はJavaScriptのquerySelectorAllです。
https://querykuma.github.io/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした