javascriptの非同期実行について、そもそもシングルスレッドなのにどうやって実現しているんだ?というところが気になったので調べてみました。
javascriptはシングルスレッドで実行されます。
つまり、複数の処理を並行に進めることができないということです。
一方、javascriptは非同期実行の仕組みを持っています。
ES2015で追加されたPromiseや、ES2017で定義されたasync/awaitなどです。
本来シングルスレッドでしか実行できない言語が、どうして非同期実行(つまり並行的な処理)ができるのでしょうか?
ポイントは
javascriptの処理とブラウザAPIの処理の違いを区別する
ということです。
こちらの記事が非常にわかりやすく、ストンと腑に落ちました。
javascriptによるプログラム実行は、ざっくり大別すると
・javascriptエンジンによる処理
・ブラウザAPIによる処理
に分かれます。
そしてよく使うsetTimeoutや、documentオブジェクトを使ったDOMの処理関連などは、実はすべてブラウザAPIが提供する機能です。
ブラウザAPIによる処理自体に、javascriptエンジンは関与しません。
つまりjavascriptエンジン内で、複数の処理を並行処理することはできませんが、javascriptエンジンによる処理とブラウザAPIによる処理が並行で進行することは可能となります。
非同期的なjavascript実行では、javascriptエンジンが上記のようなブラウザAPIを利用した処理を委任し、その間に他の処理を実行している、ということになります。
これが、JavaScriptがシングルスレッドでありながら、非同期実行を可能にしている理由です。
そして私たちが普段使うような主要なブラウザは、基本的にW3Cが標準化している仕様に則って作られています。
そのため、ブラウザが違うことでブラウザAPIが異なり、同じjavascriptのコードで違う挙動をする、といった事故も起こりづらくなっているわけですね。
それでは、例えばnode.jsなど、ブラウザを介さないjavascirptエンジンを使ったフレームワークではブラウザAPIが実装されていないのにsetTimeout諸々を使ったり、非同期処理を行ったりできるの?というところが気になりますよね。
node.jsでは、こうしたブラウザAPIが提供する機能を自前で提供しています。そのため、ブラウザと同じような環境でユーザーはjavascriptを動かすことができるということです。
まとめ
・javascriptはシングルスレッドのため、本来javascriptエンジンは並行処理ができない
・しかしブラウザAPIに処理を委任している間に、他の処理を行うことができる
・このような動きをしているのが、いわゆるjavascirptの非同期実行である
※非同期実行をより詳しく説明すると、インベントループやタスクキューといった仕組みも出てくるのですが、複雑で奥深い話題なのでここでは割愛します。
コメント