Promiseをざっくり理解する

はじめに

JavaScript嫌い・・・苦手でずっと避けてきたんですが
仕事でAWS Lambdaを使うことになり仕方なく勉強し直しました。

JavaScriptが苦手で非同期には苦労してきたので
初心者にもわかりやすい記事になっていると思います。
(私の理解ですので間違ったことを書いていればご指摘頂けると幸いです)

対象者

JavaScript初学者、いまいちPromiseasync / awaitがわからない人向けです。
非同期、同期処理について詳しく知りたい方は回れ右して下さい。

ザックリ理解したい人向けです。

それではどうぞ。

Promiseとは

  • 非同期処理を簡潔に書くことができるもの
  • コールバック地獄から解き放たれるもの
    この程度の理解で充分だと思います。

そもそも、コールバックって何?って人も多いと思います。
コールバック関数というのは、関数が実行された後に実行される関数のことです。

これだけ聞いても全くピンとこないと思いますので実際にコードを書いてみます。

(() => {

    setTimeout(() => {
        console.log("3秒経過");
    }, 3000);

})();

これを実行すると当然3秒後に「3秒経過」が出力されます。
では、3秒経過を出力した2秒後に「5秒経過」を出力してみましょう。

(() => {

    setTimeout(() => {
        console.log("3秒経過");
    }, 3000);

    setTimeout(() => {
        console.log("5秒経過");
    }, 2000);

})();

これを実行すると、、、

5秒経過
3秒経過

あれっ!?

不思議ですよね?順番通りに実行されていない。
これが非同期です。

実行自体は上から順番にされていますが、
setTimeoutは非同期のため3秒後の結果を待たず2秒後のプログラムが呼ばれます。

結果、1秒早く「5秒経過」が実行されてしまうのです。

では、どうすれば順番に出力されるのでしょうか?

(() => {

    setTimeout(() => {
        console.log("3秒経過");

        setTimeout(() => {
            console.log("5秒経過");
        }, 2000);

    }, 3000);

})();

これを実行すると、、、

3秒経過
5秒経過

ちゃんと順番通りに実行されましたね。
関数が実行されたあとに実行される関数。これがコールバック関数です。

コールバック関数があることで、非同期な処理でも同期的に実行することが可能になります。
では、何が問題なのか!?

ご覧の通り、引数の中で関数を書くためネスト構造になっています。

この程度のネストなら許容範囲かもしれませんが、
実際のプログラムでは深いネスト構造になってしまうような処理を書かなければならないことも多いハズ。
これがコールバック地獄です。

そこで誕生したのがPromiseです。

上記で書いた例をPromiseを用いて書いてみます。

(() => {

    const promise = new Promise((resolve, reject) => {

        setTimeout(() => {
            console.log("3秒経過");
            resolve();
        }, 3000);

    });

    promise.then(() => {

        setTimeout(() => {
            console.log("5秒経過");
        }, 2000);

    });

})();

これを実行すると3秒経過の後に5秒経過が出力されます。
resolve関数はPromiseを終了する関数です。引数を渡すことも可能です。
resolveしたあと、thenメソッドで次に実行したい関数を記述します。

でも、これだけじゃ旨味がわからないですよね?
しかし、以下のようにすることでthenメソッドをチェインすることができ、より非同期を直感的且つ簡潔に記述することができます。

const promiseTest = (output, delay) => {

    const promise  = new Promise((resolve, reject) => {

        setTimeout(() => {
            console.log(output);
            resolve();
        }, delay);

    });

    return promise;
};

promiseTest("3秒経過", 3000)
    .then(() => promiseTest("5秒経過", 2000))
    .then(() => promiseTest("6秒経過", 1000));

これでコールバック地獄からも開放され直感的に非同期を書くことができます。

いかがでしたか?少しPromiseについて理解できたでしょうか?
Promiseには他にもreject関数を使うケースやPromise.allといったものもあります。
詳しく知りたい方は、調べてみて下さい。

次回はasync / awaitについて書いてみます。