質問: 0;i<=10000000;i++)の重い処理が同期処理される。 17. out hen globalを出力する。 18. 最後に(2)[0, 1]を出力する。 であると考えています。 ここで気になった質問なのですが 2-1. awaitキーワードがあった時点で内部関数を実行し、その内部関数で非同期処理の内容があった瞬間、それ以降をすべてキューに移し、他のスタック処理を行うという認識はあっていますでしょうか?(挙動の4, 8などです) 2-2. 11でhen関数内に戻っていて、キューの追加順を考えると、hen関数内に戻るのはおかしいと考えているのですが、これはキューに追加されていないということなのでしょうか? 2-3. async, await, setTimeoutの挙動は同じなのでしょうか?(非同期処理という意味で一概に同じグループにして大丈夫でしょうか)
回答: > > async, await, setTimeoutの挙動は同じなのでしょうか? > > > 「promise、`await`」と「`setTimeout`」は異なる挙動をします。なぜならそれぞれ異なるキューが用意されていて、実行のされ方も異なるからです。 非同期に呼び出される関数が蓄えられるキューは2種類あり、task queue と microtask queue です。promise のコールバックと MutationObserver のコールバックは microtask queue に入れられ、それ以外の非同期なコールバック(`setTimeout` など)はすべて task queue に入れられます。(queue に入れられるタイミングはコールバックがセットされたタイミングではなくて、呼び出されほしい、という時です。例えば Promise ならそれが resolve や reject したタイミングです)。 注意: `requestAnimationFrame()`のコールバックは別のタイミングで実行されます。 あまり細かいことは自分も分かっていませんが、基本的にコールスタックが空になるとすぐに microtask checkpoint というのが実行されて、microtask queue に溜まっている microtask が古い順に**すべて実行されます**。もしその microtask が(例えば `Promise.resolve().then(...)` などで)即座に別の microtask をエンキューしたとしても、それも含めて microtask queue が空になるまで実行されます。 一方、task は**一つづつしか実行されません**。task queue から一番古い task が一つ取り出され、それを実行し終わるとすぐに microtask checkpoint が実行され、そのあと画面のレンダリングが行われ(行われないこともある)、これが繰り返されます。 JavaScript の実行は event loop というもので制御されており、これが上で述べたような task queue や microtask queue の実行、レンダリングなどを順に繰り返し実行するようになっていて、詳しくは 8.1.4.3 Processing model で定義されています。 いろいろ説明してきましたが、このプレゼンテーション動画が非常にわかりやすいのでおすすめです。英語ですが、字幕もついています。 * Jake Archibald: In The Loop - JSConf.Asia - YouTube 以上を踏まえると、例えば下のコードの実行結果はさらにその下のようになります。 ``` !(async () => { setTimeout(() => { console.log("setTimeout1") Promise.resolve("promise3").then(console.log) }) setTimeout(() => console.log("setTimeout2")) Promise.resolve("promise1").then(console.log) console.log(await "await 1") Promise.resolve("promise2").then(console.log) console.log(await "await 2") })() console.log("1") ``` ``` 1 promise1 await 1 promise2 await 2 setTimeout1 promise3 setTimeout2 ``` DEMO(JSFiddle) `await` する値が promise でなくともそれ以降の処理は microtask となり、振る舞いとしては `Promise.resolve()` と同等であることが分かると思います。また、microtask が task より前に実行されていることも観察できます。microtask `promise3` は task `setTimeout1` が終了した直後かつ、task `setTimeout2` より前に実行されています。