⚙️
JavaScript非同期処理サイクルの紙芝居
概要
JavaScriptではシングルスレッドで処理が行われる。
スレッドとはコンピューターが実行する処理の一本の流れのことであり、「シングルスレッドで処理が行われる」ということは、「複数のスレッドで並列して処理を進めることはできない」ということである。
つまり厳密に言うとJSでは並列処理は行えない。
では、一見並列処理をしているように見える非同期処理を実行している際、JSエンジンおよびブラウザ内部はどのような挙動をしているのか?
これを理解するためには、Call Stack, WebAPI, Queue, Event Loopという4つの装置をおそらく知る必要があるため、それぞれざっくり概要を書く。
JSの処理サイクルを回す装置
-
Call Stack
- プログラムの中で現在どのタスク(関数)が実行されているかを記録しているところ。つまりここに記録されている関数が現在メインスレッドで実行されているもの
- LIFO(後入れ先出し)
- 後からStackに追加されたタスクが常に先にスレッドで実行される
- JSエンジンの機能
-
WebAPI
- DOM eventやsetTimeoutなどの非同期的な関数を実行するところ
- ブラウザ内の機能
-
Queue
- 実行待ちのタスクの順番を管理するところ。ここにあるのは全てWebAPIから渡された非同期的なタスク
- FIFO(先入れ先出し)
- 先にQueueに追加されたタスクが常に先んじてEvent Loopにpushされる
- QueueにはMicro Task QueueとMacro Task Queueの2種類存在し、Micro Taskにあるタスクの方がより優先的にpushされる
- ブラウザ内の機能
-
Event Loop
- Call Stackが空の場合にQueueにあるタスクをCall Stackに渡すところ
- 「Call Stackに現在実行中のタスクがないこと」と「Queueにタスクがあるか」をループ的に確認している
- ブラウザ内の機能
- Call Stackが空の場合にQueueにあるタスクをCall Stackに渡すところ
非同期処理サイクルの紙芝居
同期処理のサイクルはとても単純で、Call Stackに関数hogeが追加されて、処理が終了し次第次の関数がまた追加される、というものである。
非同期処理のサイクルは複雑なので紙芝居で表現する。こんな感じ。
Call StackとそれぞれのQueueの優先度
処理が実行される優先度は、Call Stack > Micro Task Queue > Macro Task Queue となる。
フローとしては、シングルスレッドであるCall Stackでタスクがなくなると次はMicro Task上のタスクがCall Stackに渡され、それが全てなくなるとMacro Task上のタスクが同様に渡される。
所感
諸々調べてたら、この辺のサイクルをさらに視覚的に分かりやすく理解できそうないい感じのLoupeっていうサイトを見つけた。また使いたい!
Discussion