非同期処理 (1): Javascript の動作の流れ (JS エンジン / Call Stack / Event Queue)
Javascriptの動作
Javascriptがブラウザでどう動作しているかを確認しましょう。ここではChromeを基準にします。
V8: Javascript Engine
Javascriptのエンジンの中ではgoogleのV8エンジンがあり、V8はChromeとNode.jsで利用されます。
Heap
- メモリを管理する領域
Call Stack
- 実行するタスクを管理する領域
- 実行される関数が呼び出された瞬間ここに保存されます。シングルスレッドのため一個しかなく、一番上の積まれた関数を先に処理します。 LIFO(Last In First Out)
Web APIs
- ブラウザ側で提供するAPIでDOM, Ajax, timerなどがあります。
- Call Stackは非同期関数の実行をWeb APIにお願いします。
非同期処理は後述しますが、前の処理が終わるのと関係なく並列で処理することを指します。
WebAPIは受け取った関数を実行し、完了したら(例:setTimeoutで設定した10秒が経ったら)非同期処理のCallback関数(例:10秒が経ったら実行される関数)をEvent Queueに渡します。 - Call Stackがシングルスレッドのため、順番通りに処理しないといけませんが、WebAPIが非同期処理を担当してくれるため、あたかも並列で処理ができるように見えます。
Event Queue
Callback Queueとも呼ばれます。
Task Queueにも呼ばれましたが、Micro Task Queueの概念も出てきたため、名称が変わりました。
- Web APIから受け取ったCallback関数を保存します。Call Stackと違って、先に入ってきた関数を先に処理します。FIFO(First In First Out)
- ここに保存された作業たちは後述するEvent LoopによってCall Stackに移動されて実行されることになります。Event Queueの中でも優先順位がありますが、そこについては図を参考にしてください。
Event Loop
Call StackとEvent Queueを監視し、Call Stackに入っていた全ての作業が完了されたら、Event Queueの作業を順番にCall Stackに移動させます。この作業を繰り返します。
動作の例
では、以下のソースコードを見てみましょう。
function main(){
console.log('A');
setTimeout(
function exec(){ console.log('B'); }
,0);
console.log('C');
}
main();
結果はどうなるのでしょうか。
二つ目の関数は、setTimeoutが0秒のためすぐ実行されるから A, B, Cの順に表示されると思うかもしれませんが、結果はこうなります。
// A
// C
// B
setTimeoutは非同期処理なのでCall Stackですぐ実行されるのではなく、WebAPIの方に一回送られて0秒がたった後、Event LoopによってCall Stackに戻ります。その間にconsole.log('C')
はCallStackですぐ実行されるため、 A -> C -> B順に 結果が表示されます。
参考
JavaScriptのEvent Loop処理アニメーションはこちらのサイトに詳しく出ていますので参考してください。
同期処理 / 非同期処理
同期処理
- プログラムを書いた順番と処理する順番が一致すること
- 前の処理が終わるまで待ちます。
非同期処理
- 前の処理が終わるのと関係なく並列で処理します。
- 順番通りではないです。
ブラウザで非同期処理はできているけど、私たちは作業の順番を制御したいです。
例えば、天気のAPIから今日の天気のデータを取得して今日の天気を画面に表示したいです。
それを制御しないと天気情報はまだ取得できてないのに画面が先に表示され、元気情報がundefinedになってしまいます。
それを制御したいためプログラムでの非同期処理の実装します。
実装にはCallback関数、Promise、async / awaitなどが必要です。
これらについては次の記事に書きます。
Discussion