JavaScript については、シングルスレッドで非同期であるという漠然とした印象があります。この記事では主に JavaScript の仕組みについて説明します。しかし、その前に、まずこれらの概念を理解しましょう (今すぐ学び、今すぐ販売しましょう) 。
プロセスは、システムリソースの割り当てとスケジューリングの単位です。実行中のプログラムがプロセスに相当します。プロセスには、実行中のプログラムと、プログラムによって使用されるメモリおよびシステム リソースが含まれます。シングルコア CPU の場合、同時に実行されるプロセスは 1 つだけです。ただし、シングルコア CPU は複数のタスクを同時に実行することもできます。たとえば、NetEase Youdao Cloud Notes でブログ記事を書きながら、NetEase Cloud Music の毎日のおすすめ曲を聴くこともできます。これは 2 つのプロセスとしてカウントされます (マルチプロセス) 実行メカニズムは、しばらく曲を再生し、しばらく入力に応答するというものですが、CPU の切り替え速度が非常に速いため、まったく感じられません。そのため、これら 2 つのプロセスが同時に実行されていると考えることができます。リソースはプロセス間で分離されます。
スレッドとは何ですか?スレッドはプロセスの下の実行者であり、プロセスは少なくとも 1 つのスレッド (メイン スレッド) を開始します。また、複数のスレッドを開始することもできます。たとえば、NetEase Cloud Music はオーディオを再生し、同時に歌詞を表示します。マルチプロセスの操作は、実際にはプロセス内のスレッドを通じて実行されます。プロセス下のスレッドはリソースを共有します。複数のスレッドが同じリソースを同時に操作すると、リソースの競合が発生します。これは別の質問です。
並列処理とは、複数の処理が同時に並行して処理されるプログラムの実行状態を指します。 1 つのスレッドは同時に 1 つのことしか処理できないため、並列処理では複数のことを同時に実行するために複数のスレッドが必要です。
同時実行性とは、複数の処理を同時に交互に処理できるようにするプログラムの設計構造を指します。重要なのは、一度に 1 つのことだけが実行されるということです。たとえば、シングルコア CPU は複数のタスクを同時に実行するプロセスを実現できます。
同期と非同期は、プログラムの動作を指します。同期 (Synchronous) とは、プログラムが呼び出しを行うと、結果が返されるまで待機し、結果が返されなくなるまで戻りません。つまり、同期とは、呼び出し元が呼び出しプロセスをアクティブに待機することを意味します。
非同期とは、呼び出し後すぐに戻りますが、結果はすぐには返されません。呼び出し元は、アクティブに待つ必要はありません。呼び出し先が結果を取得すると、呼び出し元にアクティブに通知されます。
たとえば、ミルクティーショップに飲み物を買いに行きます。同期とは、顧客が必要性 (リクエスト) を述べ、ウェイターが飲み物を準備するのを待ち、注文した飲み物を受け取ってから次の顧客が上記のプロセスを繰り返すことを意味します。非同期とは、顧客が最初に注文するために列に並び、注文後は待機し、準備ができたらウェイターが番号を呼び出すことを意味します。
つまり、スレッドは同期と非同期に直接関係しておらず、単一のスレッドでも非同期を実現できます。実装方法については、以下で詳しく説明します。
ブロックと非ブロックは待機状態を指します。ブロックとは、呼び出しが待機している間、スレッドが「一時停止」されることを意味します (CPU リソースが別の場所に割り当てられます)。
ノンブロッキング (ノンブロッキング) とは、待機プロセス中に CPU リソースがまだスレッド内にあり、スレッドが他のことも実行できることを意味します。
先ほどの飲み物を買うために列に並んだ例を考えてみましょう。 ブロッキングとは、待っている間は何もできないことを意味し、待っている間は他のことができることを意味します。
つまり、同期にはブロッキングまたはノンブロッキングが可能であり、非同期にはブロッキングまたはノンブロッキングが可能です。
上記の概念を理解すると、シングルスレッドと非同期の間に矛盾がないことがわかるでしょう。では、JS はどのように実行されるのでしょうか? JS は実際には言語であり、それがシングルスレッドであるかマルチスレッドであるかは、特定の動作環境によって異なります。通常、JS はブラウザーで実行され、JS エンジンによって解析されて実行されます。ブラウザを詳しく見てみましょう。
現在最も人気のあるブラウザは、Chrome、IE、Safari、FireFox、Opera です。ブラウザのコアはマルチスレッドです。ブラウザは通常、次の常駐スレッドで構成されます:
レンダリング エンジン スレッド: 名前が示すように、このスレッドはページのレンダリングを担当します
JS エンジン スレッド: JS の解析と実行を担当します
時間指定トリガー スレッド: setTimeout、setInterval などのスケジュールされたイベントを処理します
イベント トリガー スレッド: DOM イベントを処理します
非同期 http リクエスト スレッド: http リクエストを処理します
レンダリングスレッドと JS エンジン スレッドを同時に実行することはできません。レンダリング スレッドがタスクを実行しているとき、JS エンジン スレッドは一時停止されます。 JS は DOM を操作できるため、レンダリング中に JS が DOM を処理すると、ブラウザーが混乱する可能性があります。
通常、ブラウザについて話すときは、レンダリング エンジンと JS エンジンの 2 つのエンジンについて話します。レンダリング エンジンは、Chrome/Safari/Opera では Webkit エンジンを使用し、IE では Trident エンジンを使用し、FireFox では Gecko エンジンを使用してページをレンダリングする方法です。異なるエンジンが同じスタイルを一貫して実装していないため、ブラウザのスタイル互換性の問題が発生し、しばしば批判されます。ここでは詳しく説明しません。
JS エンジンは、JS コードの解析と実行を担当する JS 仮想マシンであると言えます。通常、次のステップが含まれます:
字句解析: ソースコードを意味のある単語セグメントに分解します
構文分析: 構文アナライザーを使用して単語セグメントを構文ツリーに解析します
コード生成: 生成マシンのエネルギー コードの実行
コードの実行
ブラウザごとに JS エンジンも異なります。Chrome は V8、FireFox は SpiderMonkey、Safari は JavaScriptCore、IE は Chakra を使用します。
JS はシングルスレッドであるという話に戻りますが、基本的に、ブラウザーは実行時に JS を解析して実行するために 1 つの JS エンジン スレッドを開くだけです。では、なぜエンジンが 1 つだけなのでしょうか? DOM 上で 2 つのスレッドが同時に動作している場合、ブラウザは再び混乱するでしょうか? !
ここまでお話してきたので、いよいよ JS の実行プロセス全体について説明します。
まず、JS の同期実行プロセスがどのように実現されるかを見てみましょう。これには、実行コンテキストという非常に重要な概念が関係します。私の翻訳の 1 つである Deep Learning JavaScript Closures では、この概念について詳しく説明しています。
実行コンテキストは、コードの実行時の環境を記録します。現在の実行状態では 1 つの実行コンテキストが有効です。では、実行コンテキストには正確に何が記録されるのでしょうか?おそらく字句環境、変数環境などが考えられます。簡単な例を挙げると:
var x = 10; function foo(){ var y=20; function bar(){ var z=15; } bar(); } foo();
コードが実行され、最初にグローバル コンテキストに入ります。次に、foo()
が実行されると、もちろん、この時点ではグローバル コンテキストがまだ存在します。 bar()
を実行すると、バー コンテキストが再度入力されます。 bar()
を実行した後、foo コンテキストに戻ります。 foo()
を実行すると、グローバル コンテキストに戻ります。したがって、実行プロセスの実行コンテキストは、先入れ後出しの呼び出しスタック (呼び出しスタック) を形成します。 foo()
的时候,就进入了foo上下文,当然此时全局上下文还在。当执行 bar()
的时候,又进入了bar上下文。执行完毕bar()
,回到foo上下文。执行完foo()
,又回到全局上下文。所以,执行过程执行上下文会形成一个调用栈(Call stack),先进后出。
// 进栈 3 bar Context => => 2 foo Context => 2 foo Context 1 global Context 1 global Context 1 global Context // 出栈 3 bar Context 2 foo Context => 2 foo Context => => 1 global Context 1 global Context 1 global Context
在JS执行过程中,有且仅有一个执行上下文在起作用。因为JS是单线程的,一次只能做一件事。
以上的过程都是同步执行的。
我们回顾一下JS中自带了哪些原生的异步事件:
setTimeout
setInterval
事件监听
Ajax请求
etc..
JS异步的效果得益于浏览器的执行环境。实际上,浏览器又开了线程来处理这些BOM事件。举例:
function foo(){ console.log(1); } function bar(){ console.log(2); } foo(); setTimeout(function cb(){ console.log(3); }); bar();
按照上一节的分析,首先进入全局上下文,运行至foo()
,进入了foo上下文环境;执行console.log(1)
,控制台输出1;foo上下文环境出栈,运行至setTimeout
,交给浏览器的定时处理线程;运行至bar()
,进入了bar上下文环境;执行console.log(2)
,控制台输出2;foo上下文环境出栈;等到浏览器线程执行完setTimeout
,返回cb()
回调函数至当前任务队列;当发现执行栈为空时,浏览器的JS引擎会执行一次循环,将事件队列的队首出队至JS执行栈中;执行cb()
,进入cb上下文环境;执行console.log(3)
,控制台输出3;事件队列为空,全局上下文出栈。
以上就是JS引擎的事件循环机制,是实现异步的一种机制。主要涉及到浏览器线程,任务队列以及JS引擎。所以,我们可以看出,JS的异步请求,借助了而它所在的运行环境浏览器来处理并且返回结果。而且,这也解释了为什么那些回调函数的this
指向window
rrreee
rrreee
前のセクションの分析によると、まずグローバル コンテキストに入り、foo()
を実行して、foo コンテキスト環境に入り、console.log(1) を実行します。
の場合、コンソールは 1 を出力します。 foo コンテキストはスタックからポップされ、setTimeout
が実行され、ブラウザのタイミング処理スレッドの実行に渡されます。 bar()
に、bar コンテキストを入力します。console.log(2)
を実行すると、ブラウザ スレッドがポップするまでコンソールが 2 を出力します。 は setTimeout
の実行を終了し、実行スタックが見つかったときに cb()
コールバック関数を現在のタスク キューに返します。空の場合、ブラウザの JS エンジン >イベント キューの先頭を JS 実行スタックからデキューするループが実行され、cb()
を実行して cb コンテキスト環境に入ります。 ; console.log(3)
を実行すると、コンソールは 3 を出力し、イベント キューは空になり、グローバル コンテキストがポップされます。
this
が window
を指す理由も説明されています。 あとがき: 正しく理解できたかどうかもわかりませんし、わかりやすく説明できたかどうかもわかりません。不適切な点がございましたらご指摘ください。 以上がJavaScriptの動作仕組みと概念分析の詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。