🐥

async/await理解のためのクイズを考えてみた (後半)

に公開

はじめに

今私は、JSの非同期処理を100%理解したいと思い、以下の学習ロードマップを参考にして学習を進めています。
JSの非同期処理を理解するために必要だった知識と学習ロードマップ

今回は、その学習ロードマップの中の「Promise チェーンの構築のアンチパターンを学ぶ」という章で紹介されていた以下の動画に取り組んでいきます。
JavaScriptのasync/await 理解してますか? 説明できますか? クイズに答えてもらって良いですか?

今回は、動画の後半を進めていきます。

問題

以下の処理を、async/awaitを利用しないで、きちんと待つ関数を作りなさいという問題です。

// node14
const test = async () => {
    console.log(1);
    await new Promise((resolve) => {
        setTimeout(() => {
            console.log(2);
            resolve();
        }, 1000);
    })
}

const main = async() => {
    await test();
    console.log(3);
}

私の予想

const test = new Promise((resolve) => 
    {
        console.log(1);
        setTimeout(() => {
            console.log(2);
            resolve();
        }, 1000);
    })


const main = function () {
    test.then(() => {
        console.log(3);
    }); 
};

main();
  • testをPromiseオブジェクトをresolveで返す。
  • main関数では、thenを使ってその結果を受け取り、成功時の処理を行う。

自分の環境で挙動を確認

ん、私の環境のコードでは、ボタンが押されてからmain関数を実行するようにしているが、ボタンを押す前にtestが動いている。これは一体どういうことだ。もう一度考えてみよう。

もう一回チャレンジ

こうだ!

const test = () => {
    console.log(1);
    return new Promise((resolve) => {
        setTimeout(() => {
            console.log(2);
            resolve();
        }, 1000);
    });
};


const main = function () {
    test().then(() => {
        console.log(3);
    }); 
};

main();
  • testは関数だから、この変数は関数で定義しないといけない。
  • Promiseはオブジェクトだから、結果を伝える返り値としてPromiseを返す。

key point

ChatGPTに1つ目が間違っていた理由と、2つ目のコードの違いについて聞いてみた。

  • 1つ目のコードのtestは、「Promiseインスタンス」そのものであり、new Promise(...)はその場で即時実行される。
    → だからボタンを押す前に、testの中が実行された。

  • 2つ目のコードでは、testは関数になっていて、main関数からtestを呼び出した時に初めて、Promiseを作成&実行する。
    → そっか、コールバック関数にしないとダメなのか

  • returnでPromiseインスタンスを返す必要がある

結構初歩的なミスだったけど、thenを使って、成功時の処理を返すというのがわかったのは偉い👏

よく間違う例として以下のコードが提示されました

const test = () => {
    console.log(1);
    new Promise((resolve) => {
        setTimeout(() => {
            console.log(2);
            resolve();
        }, 1000);
    });
};


const main = async () => {
    await test();
    console.log(3);
};

このコードだと、1,3,2で出力されてしまう。

どうして違うか考えてみる。

きちんと動いた自分のコードを比較すると以下の点が異なる

  • Promiseをreturnしていない。
  • mainは、awaitでtestのPromiseの結果を待っている。

ん、でもresolveでPromiseは結果を知らせてるから、いいのでは…?
Promiseインスタンス内の処理は即実行されるが、今回その中のsetTimeoutが非同期コールバック関数だから、他の処理の実行終了を待つ。
だから、3がまず出力されるのかな。

動画内の説明

returnしていない
returnしないと、Promise結果を待っているawaitが結果を受け取れない。

答え

私の1つ目のコードが正解だった!

const test = () => {
    console.log(1);
    return new Promise((resolve) => {
        setTimeout(() => {
            console.log(2);
            resolve();
        }, 1000);
    });
};


const main = function () {
    test().then(() => {
        console.log(3);
    }); 
};

main();

まとめ

  • Promiseはreturnで返さないと、awaitでは結果を受け取れない
  • async/awaitを使わないとき、成功時の結果後の処理では、then()を使ってその後の処理をかく。

Discussion