【javascript】async、awaitの正確な仕様を理解する

前提知識

Microtask queueMacrotask queueー非同期処理が格納されるキュー。setTimeoutはMacrotask、Promiseのコールバック関数はMicrotask queueである。

・処理の優先順位は、コールスタックに入れられた関数>Microtask queue>Macrotask queue。コールスタックが空になったとき、Microtask queueが呼ばれ、その両方が空になったとき、Macrotask queueが呼ばれる。

・要するに、大まかに言うと、処理の順番は、「非同期じゃないコード」→「Microtask queueに入れられた関数(キュー入れられた順)」→「Macrotask queueに入れられた関数(キューに入れられた順)」である。(途中で優先度の高い処理が追加されると前に戻る)

仕様① awaitが読み込まれたときの挙動

・awaitの行に差し掛かった時、awaitの右に書かれた処理を実行した後、async関数を中断し、async関数を「Microtask queue」に入れる。

以下はコード例。

async function hello()
{
    console.log("hello")
    var b = await 1
    console.log("hello"+b)
}

console.log("start")
hello()
console.log("end")

上を実行すると、start→hello→end→hello1の順でログが出力される。

仕様② async関数の返り値

・async関数の返り値Promiseオブジェクト。

・async内の処理が終わっていない場合は、stateが「Pending」であるPromiseを返す。

・async内の処理が終わっている場合は、stateが「Fulfilled」であるPromiseを返す。

returnは関数の返り値とはならない。返り値は常にPromiseオブジェクトで、その中にreturnの結果が保存される。

以下はコード例。

async function hello()
{
    var b = await 1
    console.log("resume!")
    return b
}

a = hello()
console.log(a)

window.setTimeout(() => {
    console.log(a)
}, 1000)

上を実行すると、stateが「Pending」であるPromiseオブジェクトが出力された後、「resume!」が出力され、1秒後にstateが「Fulfilled」であるPromiseオブジェクトが出力される。

仕様③ 実行順序に気を付ける

・実は、仕様②で書いたコードのsetTimeoutの秒数を0にしても、結果は同じです。

・何故なら、「Microtask queue」→「Macrotask queue」の順に実行されるからです。