1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[JavaScript]イベントループについて

Last updated at Posted at 2024-10-06

LinkedInを見ていたら以下のような場合、どういう順番でログが出力されるのかについて問題を出しているポストがあった。
普段はvueを使っているのでvueのライフサイクルはある程度わかっているつもりだったが、JavaScriptのライフサイクルは全然わからなかった。

console.log("A");

setTimeout(() => {
    console.log("B");
}, 0);

Promise.resolve().then(() => {
    console.log("C");
}).then(() => {
    console.log("D");
});

console.log("E");

答えから先に言うと、上の場合はAECDBの順番で出力される。

jsのライフサイクル

まず、jsのイベントループを理解するにあたって、コールスタックという概念が核になる。

コールスタックとは、プログラムの実行順序を管理するためのデータ構造である。

スタックは「後入先出」(LIFO: Last In, First Out)の原則で動作し、最後に追加された処理が最初に実行される。

コールスタックは、関数が呼び出されるたびに、その関数の情報をスタックの一番上に追加しする。関数の実行が完了すると、その情報はスタックから取り除かれていく。

タスクの種類

そこで、jsでは非同期処理が可能であるため、タスクごとに実行されるタイミングを調整する必要がある。そのため、タスクは以下のような種類で分けられる。

  1. 同期タスク:即時実行
  2. マイクロタスク:PromisequeueMicrotask()MutationObserver
  3. マクロタスク:setTimeoutsetIntervalrequestAnimationFrameI/O操作

実行順序

同期タスク → マイクロタスク → マクロタスク

実はコールスタック以外にもjsにはコールバックキューとマイクロキューというものがある。

コールバックキュー(タスクキューとも呼ばれる) = マクロタスク用
マイクロキュー = マイクロタスク用

だと理解するとわかりやすい。

以下のような流れで順番通りに実行されるようになっている。

  1. スクリプトを読み込む
  2. 同期タスクであれば実行する
  3. マクロタスクだったらコールバックキューに、マイクロだったらマイクロキューに入れる(実行はしない)
  4. スクリプトが終わるとマイクロキューのタスクを実行する
  5. マイクロキューが終わるとコールバックキューを実行する

具体例

ということで、自分なりにタスクを並べて、実行される順番を考えてみた。

console.log('1. スクリプトスタート');

setTimeout(() => console.log('2. setTimeout 0'), 0);

Promise.resolve().then(() => console.log('3. Promise.resolve'));

queueMicrotask(() => console.log('4. queueMicrotask'));

Promise.resolve().then(() => {
    console.log('5. Nested Promise.resolve');
    setTimeout(() => console.log('6. Nested setTimeout'), 0);
});

async function asyncFunction() {
    console.log('7. async関数内');
    await Promise.resolve();
    console.log('8. await後');
}

asyncFunction();

requestAnimationFrame(() => console.log('9. requestAnimationFrame'));

console.log('10. スクリプトおわり');

// 出力順番:

// 同期タスク
// 1. スクリプトスタート
// 10. スクリプトおわり

// マイクロタスク
// 3. Promise.resolve
// 4. queueMicrotask
// 5. Nested Promise.resolve
// 7. async関数内
// 8. await後

// マクロタスク
// 2. setTimeout 0
// 6. Nested setTimeout
// 9. requestAnimationFrame

おわりに

vueのライフサイクルよりこっちを先に勉強するべきだった。
今まで非同期処理がどうなっていたかも知らずにやってきたとは...

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?