JavaScriptはシングルスレッド言語、いわゆるシングルスレッド、同期または非同期です

JavaScriptシングルスレッド言語、いわゆるシングルスレッドです

 

JS非同期メカニズム

JavaScriptこれはシングルスレッド言語です。いわゆるシングルスレッドとは、一度に1つのタスクしか完了できないことを意味します。複数のタスクがある場合は、キューに入れる必要があります。前のタスクが完了してから、後者のタスクが実行されます。このモードの利点は、実装が比較的簡単で、実行環境が比較的単純なことです。欠点は、長時間かかるタスクがある限り、後続のタスクをキューに入れなければならず、プログラム全体の実行が遅延することです。一般的なブラウザは応答しません。これは誤った死の状態です。多くの場合Javascript、無限ループなどの特定のコードが長時間実行され、ページ全体がこの場所で動かなくなり、他のタスクを実行できないためです。

実行メカニズム

上記の問題を解決するためにJavascript、タスクの実行モードは、同期Synchronousと非同期Asynchronous、同期または非同期の2つのタイプに分けられます。これは、プロセス全体を順番に完了する必要があるかどうか、ブロッキングまたはノンブロッキングを示します。つまり、呼び出す関数は実行しません。結果をすぐに教えて

同期

同期モードは同期ブロッキングです。後者のタスクは、前のタスクの終了を待って実行します。プログラムの実行順序は、タスクの順序と一致しています。

コピーする
var i = 100;
while(--i) { console.log(i); }
console.log("while 执行完毕我才能执行"); 

非同期

非同期実行は非ブロッキングモードで実行されます。各タスクには1つ以上のコールバック関数がありますcallback。前のタスクが終了すると、次のタスクを実行する代わりにコールバック関数が実行されます。後者のタスクは、前のタスクの終了を待たずに実行されます。 、したがって、プログラムの実行順序とタスクの順序には一貫性がなく、非同期です。ブラウザTab、それぞれJs1つのスレッドのみを割り当てます。主なタスクは、ユーザーと対話して操作DOMすることなどです。これにより、1つのスレッドしかできないと判断されます。それ以外の場合は、非常に複雑な同期の問題が発生します。たとえば、JavaScript2つのスレッドがあると想定します、スレッドDOM特定のノードにコンテンツ追加し、別のスレッドがノードを削除すると、ブラウザはどのスレッドの操作が優先されるかを判断できません。

コピーする
setTimeout(() => console.log("我后执行"), 0); // 注意:W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms,此外这与浏览器设定、主线程以及任务队列也有关系,执行时间可能大于4ms,例如老版本的浏览器都将最短间隔设为10毫秒。另外,对于那些DOM的变动尤其是涉及页面重新渲染的部分,通常不会立即执行,而是每16毫秒执行一次。这时使用requestAnimationFrame()的效果要好于setTimeout()。 console.log("我先执行"); 

非同期メカニズム

最初に例を見て、上記のように非同期に実行される操作をテストします

コピーする
setTimeout(() => console.log("我在很长时间之后才执行"), 0); var i = 3000000000; while(--i) { } console.log("循环执行完毕"); 

ローカルテストでは、setTimeoutコールバック関数セット30saboutの後に実行されます4msこれはそれよりはるかに大きくなります。Jsメインスレッドに非常に大きなループを設定して、メインスレッドをブロックします。ここでは、無限ループを設定していません。ここで無限ループを設定した場合メインスレッドをブロックするために、setTimeoutコールバック関数は実行されません。また、レンダリングスレッドとJSエンジンスレッドは相互に排他的であるためJs、スレッドがタスクを処理している間、レンダリングスレッドは中断され、ページ全体がブロックされ、更新できません。ページを閉じることはできません。ページを閉じるには、タスクマネージャを使用してTabプロセスを終了する必要があります。
Js非同期は、実行スタックとタスクキューを通じて実現され、非同期操作を完了します。すべての同期タスクはメインスレッドで実行され、実行スタックを形成します。タスクキューには、さまざまなイベントコールバック(メッセージとも呼ばれます)が格納されます。実行スタックでのタスク処理が完了すると、メインスレッドがタスクキュー内のタスクの読み取りを開始して実行し、ループを継続的に往復させます。
たとえば、上記の例のsetTimeout完了したイベントコールバックはタスクキューに格納されます。エンジンはシングルスレッドJavaScriptであるため、ブラウザタイマーカウンターはエンジンによってカウントされないことに注意してくださいJavaScript。スレッドがブロックされている場合、タイミングに影響します。正確には、カウントはブラウザスレッドによってカウントされます。カウントが完了すると、イベントコールバックがタスクキューに追加されます。同様HTTPに、ブラウザには別のスレッドがあり、イベントコールバックは実行後にタスクキューに配置されます。このプロセスを通じてsetTimeout上記の例のコールバック実行できなかった理由を説明できます。これは、メインスレッド、つまり実行スタックのコードが完了していないため、このイベントコールバックがすでに実行されている場合でも、実行するタスクキューのイベントコールバックを読み取らないためです。タスクキュー。

イベントループ

メインスレッドがタスクキューイベントから読み込んで、プロセスは常にので、このメカニズムの全体的な動作も知られ、循環されEvent LoopEvent Loop別の場所で異なる方法で実装エグゼクティブモデルであり、ブラウザとNodeJS異なる技術に基づいて実装します彼ら自身のEvent Loopブラウザは、Event LoopでありHTML5、明確に定義された仕様に基づいて実装。ブラウザの実行スタック、バックグラウンドスレッド、マクロキュー、マイクロキュー構成されますNodeJSEvent Looplibuv
Event LoopExecution StackBackground ThreadsMacrotask QueueMicrotask Queue

  • 実行スタックは、メインスレッドで同期タスクを実行するデータ構造であり、関数呼び出しは複数のフレームで構成されるスタックを形成します。
  • バックグラウンドスレッドを実現するためのブラウザでsetTimeoutsetIntervalXMLHttpRequestおよび実行のスレッドでそう。
  • マクロキュー、非同期タスクのいくつかは、フォローアップと呼ばれるのを待って、マクロコールバックキューに変わります、を含むsetTimeoutsetIntervalsetImmediate(Node)requestAnimationFrameUI renderingI/Oおよびその他の操作
  • Microqueue、他の非同期タスクには、フォローアップコールを待って、マイクロコールバックキューに変わりますPromiseprocess.nextTick(Node)Object.observeMutationObserverおよびその他の操作

ときにJs実行され、次のプロセス

  1. まず、実行スタック内のコードが同期的に実行され、これらのコード内の非同期タスクがバックグラウンドスレッドに追加されます
  2. 実行スタック内の同期コードが実行された後、実行スタックが空になり、マイクロキューがスキャンされます
  3. マイクロキューの最初のタスクを実行スタックに入れて実行します。このとき、マイクロキューはデキューされます
  4. 実行スタックが完了したら、micro queueタスクがすべて実行されるまで、micro queueタスクのデキューと実行を続けます。
  5. 最後のマイクロキュータスクがデキューされて実行スタックに入ると、マイクロキュー内のタスクは空になります。実行スタックタスクが完了すると、マイクロキューはサーフェスのスキャンを開始します。マクロキュータスクのスキャンを続行します。実行が途中で、実行が完了した後、microキューが空であるのをスキャンし続け、次にマクロキューをスキャンし、実行をデキューします。
  6. 行ったり来たりして......

コピーする
// Step 1
console.log(1);

// Step 2
setTimeout(() => { console.log(2); Promise.resolve().then(() => { console.log(3); }); }, 0); // Step 3 new Promise((resolve, reject) => { console.log(4); resolve(); }).then(() => { console.log(5); }) // Step 4 setTimeout(() => { console.log(6); }, 0); // Step 5 console.log(7); // Step N // ... // Result /* 1 4 7 5 2 3 6 */ 
ステップ1
コピーする
// 执行栈 console
// 微队列 []
// 宏队列 []
console.log(1); // 1 
ステップ2
コピーする
// 执行栈 setTimeout
// 微队列 []
// 宏队列 [setTimeout1]
setTimeout(() => { console.log(2); Promise.resolve().then(() => { console.log(3); }); }, 0); 
ステップ3
コピーする
// 执行栈 Promise
// 微队列 [then1]
// 宏队列 [setTimeout1]
new Promise((resolve, reject) => { console.log(4); // 4 // Promise是个函数对象,此处是同步执行的 // 执行栈 Promise console resolve(); }).then(() => { console.log(5); }) 
ステップ4
コピーする
// 执行栈 setTimeout
// 微队列 [then1]
// 宏队列 [setTimeout1 setTimeout2]
setTimeout(() => { console.log(6); }, 0); 
手順5
コピーする
// 执行栈 console
// 微队列 [then1]
// 宏队列 [setTimeout1 setTimeout2]
console.log(7); // 7 
手順6
コピーする
// 执行栈 then1
// 微队列 []
// 宏队列 [setTimeout1 setTimeout2]
console.log(5); // 5 
手順7
コピーする
// 执行栈 setTimeout1
// 微队列 [then2]
// 宏队列 [setTimeout2]
console.log(2); // 2 Promise.resolve().then(() => { console.log(3); }); 
手順8
コピーする
// 执行栈 then2
// 微队列 []
// 宏队列 [setTimeout2]
console.log(3); // 3 
手順9
コピーする
// 执行栈 setTimeout2
// 微队列 []
// 宏队列 []
console.log(6); // 6 

参照番号

コピーする
https://www.jianshu.com/p/1a35857c78e5
https://segmentfault.com/a/1190000016278115
https://segmentfault.com/a/1190000012925872 https://www.cnblogs.com/sunidol/p/11301808.html http://www.ruanyifeng.com/blog/2014/10/event-loop.html https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/EventLoop

おすすめ

転載: www.cnblogs.com/Leo_wl/p/12737282.html