イベントループとタスクキューを理解する
目的
イベントループ、タスクの理解が曖昧なので整理したい。
ログを見ながら確認
まずは初歩的なところから
下記のコードは、どの順番でログ出力されるのかを考える。
Promise.resolve().then(() => console.log(1));
queueMicrotask(() => console.log(2));
setTimeout(() => console.log(3), 0);
console.log(4);
new Promise(() => console.log(5));
async () => console.log(6);
正解は
4, 5, 6, 1, 2, 3
実行手順を見ていく
図はcanvaで書きました。
-
まずは1行目の
Promise.resolve()
が呼び出され、コールスタックに格納される。これはすぐに解決されるプロミスであるため、.then()
がすぐにスタックにプッシュされ、マイクロタスク キューにコールバック関数の() => console.log(1)
がスケジューリングされる。
-
次に
queueMicrotask()
を呼び出し、コールスタックに格納される。これによりマイクロタスク キューにコールバック関数の() => console.log(2)
がスケジューリングされる。
-
次に
setTimeout()
がコールスタックに格納され、そのコールバックがWeb APIにスケジューリングされる。
-
setTimeout()
コールバック関数の() => console.log(3)
がマクロタスク キューにスケジューリングされると同時に、console.log(4)
がコールスタックにプッシュされる。これは単なる通常の関数なので、4がまず最初に出力される。
-
次の
new Promise()
コンストラクタでは、() => console.log(5)
は同期的に実行される。あくまでも結果部分(thenなど)を非同期で実行するため。したがって、console.log(5)
がコールスタックにプッシュされ、2番目に出力されるのは5となる。
-
次に、
async () => console.log(6)
が呼び出されるが、() => console.log(6)
は同期的に実行される。あくまでも非同期で実行される部分はawait
から始まっているものであるため。したがってconsole.log(6)
がコールスタックにプッシュされ、3番目に出力されるのは6となる。
-
コールスタックが空になったので、マイクロタスク キューの最初のタスクがコールスタックに追加される。したがって
console.log(1)
がコールスタックにプッシュされ、4番目に出力されるのは1となる。
-
同様に、
console.log(2)
がコールスタックにプッシュされ、5番目に出力されるのは2となる。
-
最後に、マクロタスク キューがコールスタックに追加される。したがって
console.log(3)
がコールスタックにプッシュされ、最後に出力されるのは3となる。
終わりに
図を用いることで、自分なりに整理できた。
少し長くなったので、今回はここまでに🙇♂️
Discussion