エンジンが同期コード、マイクロタスク、マクロタスクを実際にどうスケジュールするのかを理解します。
このレッスンを Kodokon で開くJavaScript はコードを単一のスレッドで実行しますが、イベントループは ECMAScript で定義されているわけではありません。その動作を記述しているのは HTML 仕様(そして Node.js 側では libuv)です。エンジンは、現在実行中のコードのためのコールスタック、マクロタスク(タイマー、I/O、イベント)のキュー、そしてマイクロタスク(Promise.prototype.then、queueMicrotask、MutationObserver)のキューを保持します。黄金律はこうです。各マクロタスクの後、制御が返される前にマイクロタスクキューは完全に空にされる、ということです。
console.log("script start");
setTimeout(() => console.log("timeout"), 0);
Promise.resolve()
.then(() => console.log("promise 1"))
.then(() => console.log("promise 2"));
console.log("script end");
// script start, script end,
// promise 1, promise 2, timeout最初のスクリプト自体が 1 つのマクロタスクです。それが終わるまで、他には何も実行されません。コールスタックが空になると、エンジンはマイクロタスクキューを空にします。空にしている最中に追加されたマイクロタスクも含めてです。この詳細が、連鎖した 2 つの then 呼び出しが setTimeout(fn, 0) より先に実行される理由を説明します。各 then は別のマイクロタスクをスケジュールし、キューが空になるまで制御は決して返されないのです。
setTimeout(() => console.log("macro 1"), 0);
setTimeout(() => console.log("macro 2"), 0);
queueMicrotask(() => {
console.log("micro 1");
queueMicrotask(() => console.log("micro 2"));
});
// micro 1, micro 2, macro 1, macro 2await キーワードはこの仕組みの上に成り立っています。await は async 関数を中断し、呼び出し元に制御を返してから、再開をマイクロタスクとしてスケジュールします。await null でさえこの中断を引き起こします。なぜなら、その値は解決済みの Promise でラップされるからです。これが、await の後に置かれたコードが決して同期的には実行されない理由です。
async function main() {
console.log("before await");
await null;
console.log("after await");
}
main();
console.log("sync code");
// before await, sync code, after awaitC を同期的にログ出力し、A を setTimeout(fn, 0) で、B を Promise.resolve().then でスケジュールします。文字が現れる順序はどれですか?await は内部で実際に何をしていますか?