JavaScript:同期、非同期、イベントサイクル
5704 ワード
一.単スレッド
私たちはよく「JavaScriptはシングルスレッドのもの」と言います.
シングルスレッドとは、JSエンジンでJavaScriptコードを解釈し実行するスレッドが一つしかないことを意味します.メインスレッドと呼んでもいいです.
しかし、実際には他のスレッドも存在する.例えば、AJAX要求を処理するスレッド、DOMイベントを処理するスレッド、タイマースレッド、ファイルを読み書きするスレッド(例えば、Node.js)などがある.これらのスレッドはJSエンジン内に存在するかもしれないし、JSエンジン以外に存在するかもしれない.ここでは区別しない.それらを作業スレッドと呼んでもいいです.
二.同期と非同期
関数Aが存在すると仮定します.
たとえば:の最初の関数が戻ると、予想される戻り値:2の平方根が得られます. の2番目の関数が戻ると、予想される効果が見られます.コンソールで文字列が印刷されました. この二つの関数は同期しています.
非同期:もし関数Aが戻ってきた時に、使用者はまだ予想される結果を得ることができず、将来に一定の手段で得る必要があるなら、この関数は非同期です.
たとえば:
しかし、fs.readFile関数が戻ってきたら、私たちが望む結果は発生しません.ファイルが全部読み込まれるまで待つべきです.書類が大きいと長い時間がかかります.
AJAX要求を例にとって、同期と非同期の違いを見てみます.
非同期AJAX: 主スレッド「こんにちは、AJAXスレッド.HTTPをお願いします.住所とパラメータを全部渡しました.」(続いて、メインスレッドは他のことをして行きました.食事の時間後、到着の知らせがありました.)
同期AJAX: メインスレッド「こんにちは、AJAXスレッド.HTTPをお願いします.要求住所とパラメータを全部あげました.」
AJAXスレッド:「…」メインスレッド:「もしもし、AJAXスレッド、どうして話さないのですか?」AJAXスレッド:「…」メインスレッド:「もしもし、もしもし!」AJAXスレッド:「…」(一束の香の時間後)メインスレッド:「もしもし、お願いします.」AJAX線程:「メインスレッド、すみません、仕事中は話ができませんでした.あなたの要請はもう終わりました.応答データを持ってきました.どうぞ.」
JavaScriptはシングルスレッドであり、非ブロッキングを容易に実現するため、Java Scriptでは時間のかかる操作や時間の不確定な操作に対して、非同期を使うことが必然的な選択となります.
三.非同期過程の構成要素
上記から分かるように、
まとめてみます.非同期のプロセスは通常このようなものです.
主スレッドは、要求を受信して、主スレッドが受信したことを通知する非同期要求を開始します.主スレッドは、後のコードを実行しながら、作業スレッドは非同期タスクを実行します.作業スレッドが完了したら、メインスレッドに通知します.主スレッドが通知を受信したら、一定の動作を実行します.
非同期関数は、通常以下の形式を有する.
したがって、主スレッドの観点から、非同期プロセスは次の2つの要素を含む.開始関数(または登録関数)A コールバック関数 コールバックFn これらは、非同期プロセスを開始するために登録関数を使用し、結果を処理するためにコールバック関数を使用するメインスレッド上で起動される.
具体的な例を挙げます.
注意:前に述べたような形 A(args…、calbackFn) 抽象的な表現にすぎません.コールバック関数は必ずイニシャル関数のパラメータとして必要です.例えば、
四.メッセージキューとイベントサイクル
上述したように、非同期プロセスでは、非同期動作が完了したら、メインスレッドに通知する必要があります.この通知メカニズムはどのように実現されますか?答えはメッセージ・キューとイベント・サイクルを利用しています.
一言で要約すると:
作業スレッドはメッセージをメッセージキューに置き、メインスレッドはイベントサイクルプロセスを通してメッセージを取りに行く.メッセージ・キュー:メッセージ・キューは、先着順のキューであり、様々なメッセージが格納されています. イベントサイクル:イベントサイクルとは、メインスレッドがメッセージキューからメッセージを取り出して実行するプロセスを繰り返すことを意味する. 実際には、メインスレッドは一つのことしかできません.メッセージキューからメッセージを取り、実行し、メッセージを取り、再実行します.メッセージキューが空になったら、メッセージキューが空になるまで待ちます.また、メインスレッドは現在のメッセージを実行した後だけ、次のメッセージを取りに行きます.このような仕組みはイベントループ機構といいます.メッセージを取ってください.実行する過程を一回の循環といいます.
イベントのループはコードで表しています.
メッセージは非同期タスクを登録する際に追加されるコールバック関数です.
再度非同期AJAXを例にとって、下記のコードが存在すると仮定します.
メインスレッドは現在のループのすべてのコードを実行した後、メッセージキューにメッセージを取り出します. メッセージ 関数)を実行します.これまでにワークスレッドのメインスレッド
この過程を図で表します.
上記からもこのような明確な結論が得られます.
非同期プロセスのコールバック関数は、現在のイベントサイクルでは必ず実行されません.
五.非同期と事件
上記の「イベントサイクル」には、なぜ
メッセージ・キューの各々は、実際には一つのイベントに対応している.
上記では、DOMイベントのような重要な非同期プロセスは言及されていない.
例えば:
非同期過程の角度から見て、 addEvent Listener 関数は非同期プロセスの開始関数であり、イベントモニター関数は非同期プロセスのコールバック関数である.イベントがトリガされると、非同期タスクが完了したことを示し、イベントモニター関数をメッセージキューに入れて、メインスレッドの実行を待つ.
イベントの概念は実際には必要ではないです.イベントのメカニズムは実際には非同期プロセスの通知メカニズムです.プログラミングインターフェースのために開発者により友好的な存在だと思います.
一方、すべての非同期プロセスは、イベントによって記述されてもよい.例えば、
生産者と消費者の立場から見て、非同期の過程はこうです.
作業スレッドは生産者であり、主スレッドは消費者である(消費者は一人しかいない).作業スレッドは非同期タスクを実行し、実行が完了したら対応するコールバック関数をメッセージキューにカプセル化し、メインスレッドはメッセージキューからメッセージを取り出して実行し、メッセージキューが空である時にメインスレッドがブロックされ、メッセージキューが再び空でない時まで停止する.
PS:ECMAScript 262仕様には、非同期、イベントキューなどの概念およびその実装についての説明はありません.これらは、具体的なJavaScriptが動作する際に環境によって使用されるメカニズムです.ここでは、非同期プロセスの原理を説明することに重点を置いています.理解を容易にするために、多くの簡略化がなされています.したがって、いくつかの用語の使用は、正確ではないかもしれません.具体的な詳細は、正確ではないかもしれません.例えば、メッセージのキューにあるメッセージの構成について、ご注意ください.
私たちはよく「JavaScriptはシングルスレッドのもの」と言います.
シングルスレッドとは、JSエンジンでJavaScriptコードを解釈し実行するスレッドが一つしかないことを意味します.メインスレッドと呼んでもいいです.
しかし、実際には他のスレッドも存在する.例えば、AJAX要求を処理するスレッド、DOMイベントを処理するスレッド、タイマースレッド、ファイルを読み書きするスレッド(例えば、Node.js)などがある.これらのスレッドはJSエンジン内に存在するかもしれないし、JSエンジン以外に存在するかもしれない.ここでは区別しない.それらを作業スレッドと呼んでもいいです.
二.同期と非同期
関数Aが存在すると仮定します.
A(args...);
同期:関数Aが戻ってきたときに、スケジューラが予想される結果を得ることができる(すなわち、期待された戻り値を得るか、または予期された効果を見ることができる)なら、この関数は同期されます.たとえば:
Math.sqrt(2);
console.log('Hi');
非同期:もし関数Aが戻ってきた時に、使用者はまだ予想される結果を得ることができず、将来に一定の手段で得る必要があるなら、この関数は非同期です.
たとえば:
fs.readFile('foo.txt', 'utf8', function(err, data) {
console.log(data);
});
上のコードの中で、ファイルのfoo.txtの内容をfs.readFile関数で読み、プリントしたいです.しかし、fs.readFile関数が戻ってきたら、私たちが望む結果は発生しません.ファイルが全部読み込まれるまで待つべきです.書類が大きいと長い時間がかかります.
AJAX要求を例にとって、同期と非同期の違いを見てみます.
AJAXスレッド:「…」メインスレッド:「もしもし、AJAXスレッド、どうして話さないのですか?」AJAXスレッド:「…」メインスレッド:「もしもし、もしもし!」AJAXスレッド:「…」(一束の香の時間後)メインスレッド:「もしもし、お願いします.」AJAX線程:「メインスレッド、すみません、仕事中は話ができませんでした.あなたの要請はもう終わりました.応答データを持ってきました.どうぞ.」
JavaScriptはシングルスレッドであり、非ブロッキングを容易に実現するため、Java Scriptでは時間のかかる操作や時間の不確定な操作に対して、非同期を使うことが必然的な選択となります.
三.非同期過程の構成要素
上記から分かるように、
は、実際にはすぐに呼び出しが完了しました.しかし、後にはワークスレッドが非同期タスクを実行し、メインスレッドに通知し、コールバック関数などの多くのステップがあります.全プロセスを
と呼んでいます.非同期関数の呼び出しは、全体の非同期プロセスの中で、ほんの一部です.まとめてみます.非同期のプロセスは通常このようなものです.
主スレッドは、要求を受信して、主スレッドが受信したことを通知する非同期要求を開始します.主スレッドは、後のコードを実行しながら、作業スレッドは非同期タスクを実行します.作業スレッドが完了したら、メインスレッドに通知します.主スレッドが通知を受信したら、一定の動作を実行します.
非同期関数は、通常以下の形式を有する.
A(args..., callbackFn)
非同期プロセスの開始関数、または非同期タスク登録関数と呼ばれることができます.args
はこの関数に必要なパラメータです.callbackFn
もこの関数のパラメータですが、それは特別なので単独で並べられます.したがって、主スレッドの観点から、非同期プロセスは次の2つの要素を含む.
具体的な例を挙げます.
setTimeout(fn, 1000);
その中の set Timeout 非同期プロセスの開始関数です. fn は、コールバック関数です注意:前に述べたような形 A(args…、calbackFn) 抽象的な表現にすぎません.コールバック関数は必ずイニシャル関数のパラメータとして必要です.例えば、
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = xxx; //
xhr.open('GET', url);
xhr.send(); //
開始関数とコールバック関数は分離されます.四.メッセージキューとイベントサイクル
上述したように、非同期プロセスでは、非同期動作が完了したら、メインスレッドに通知する必要があります.この通知メカニズムはどのように実現されますか?答えはメッセージ・キューとイベント・サイクルを利用しています.
一言で要約すると:
作業スレッドはメッセージをメッセージキューに置き、メインスレッドはイベントサイクルプロセスを通してメッセージを取りに行く.
イベントのループはコードで表しています.
while(true) {
var message = queue.get();
execute(message);
}
メッセージ・キューに入れられているメッセージは具体的に何ですか?メッセージの具体的な構造はもちろん具体的な実現に関連していますが、簡単にするために、以下のように考えられます.メッセージは非同期タスクを登録する際に追加されるコールバック関数です.
再度非同期AJAXを例にとって、下記のコードが存在すると仮定します.
$.ajax('http://segmentfault.com', function(resp) {
console.log(' :', resp);
});
//
...
...
...
メインスレッドはAJAX要求を開始した後、他のコードを実行し続けます.AJAXスレッドはsegmentfault.comを要求します.応答を取ったら、JavaScriptオブジェクトに応答をカプセル化し、メッセージを作成します.//
var message = function () {
callbackFn(response);
}
その中の コールバックFn 前のコードで成功応答を得た場合のコールバック関数です.メインスレッドは現在のループのすべてのコードを実行した後、メッセージキューにメッセージを取り出します. メッセージ 関数)を実行します.これまでにワークスレッドのメインスレッド
が完了しました.コールバック関数も実行されました.最初のスレッドにはコールバック関数が提供されていません.HTTP応答を受信したAJAXスレッドは、メインスレッドに通知する必要はなく、メッセージキューにメッセージを送る必要もありません.この過程を図で表します.
上記からもこのような明確な結論が得られます.
非同期プロセスのコールバック関数は、現在のイベントサイクルでは必ず実行されません.
五.非同期と事件
上記の「イベントサイクル」には、なぜ
が含まれているのですか?メッセージ・キューの各々は、実際には一つのイベントに対応している.
上記では、DOMイベントのような重要な非同期プロセスは言及されていない.
例えば:
var button = document.getElement('#btn');
button.addEventListener('click', function(e) {
console.log();
});
イベントの観点から上記のコードは、ボタンにマウスクリックイベントのイベントモニターを追加し、ユーザーがボタンをクリックすると、イベントのトリガをクリックして、イベントモニター関数を呼び出します.非同期過程の角度から見て、 addEvent Listener 関数は非同期プロセスの開始関数であり、イベントモニター関数は非同期プロセスのコールバック関数である.イベントがトリガされると、非同期タスクが完了したことを示し、イベントモニター関数をメッセージキューに入れて、メインスレッドの実行を待つ.
イベントの概念は実際には必要ではないです.イベントのメカニズムは実際には非同期プロセスの通知メカニズムです.プログラミングインターフェースのために開発者により友好的な存在だと思います.
一方、すべての非同期プロセスは、イベントによって記述されてもよい.例えば、
setTimeout
は、対応する時間が到来したイベントと見なされてもよい.前文の set Timeout(fn,1000) ビュー:timer.addEventListener('timeout', 1000, fn);
六.生産者と消費者生産者と消費者の立場から見て、非同期の過程はこうです.
作業スレッドは生産者であり、主スレッドは消費者である(消費者は一人しかいない).作業スレッドは非同期タスクを実行し、実行が完了したら対応するコールバック関数をメッセージキューにカプセル化し、メインスレッドはメッセージキューからメッセージを取り出して実行し、メッセージキューが空である時にメインスレッドがブロックされ、メッセージキューが再び空でない時まで停止する.
PS:ECMAScript 262仕様には、非同期、イベントキューなどの概念およびその実装についての説明はありません.これらは、具体的なJavaScriptが動作する際に環境によって使用されるメカニズムです.ここでは、非同期プロセスの原理を説明することに重点を置いています.理解を容易にするために、多くの簡略化がなされています.したがって、いくつかの用語の使用は、正確ではないかもしれません.具体的な詳細は、正確ではないかもしれません.例えば、メッセージのキューにあるメッセージの構成について、ご注意ください.