ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript シングルスレッドメカニズムと setTimeout 実行原理の概要 (コード付き)

JavaScript シングルスレッドメカニズムと setTimeout 実行原理の概要 (コード付き)

不言
不言転載
2019-03-27 09:09:013384ブラウズ

この記事では、JavaScript のシングル スレッド メカニズムと setTimeout の実行原理 (コード付き) を紹介します。これには一定の参考値があります。必要な友人は参照できます。お役に立てれば幸いです。

JavaScript エンジンのシングルスレッド メカニズム

まず、JavaScript エンジンがシングルスレッド メカニズムであることは明らかです。

JavaScript は単一のスレッドで実行され、複数のコードを同時に実行することはできません。特定のコードが実行されているとき、後続のすべてのタスクは待機する必要があり、タスク キューを形成します。現在のタスクが実行されると、次のタスクがキューから取り出されます。これは、多くの場合「実行のブロック」と呼ばれます。

これは次のように理解できます: JS スレッドで実行する同期コードがない場合にのみ非同期コードが実行されます

つまり、マウスをクリックするか、タイマーが時点に達するか、または Ajaxリクエストが完了する コールバック関数がトリガーされると、これらのイベント ハンドラーまたはコールバック関数はすぐには実行されませんが、すぐにキューに入れられ、スレッドが解放されると実行されます。現在の JavaScript スレッドが時間のかかるコードを実行しているときにマウス クリックが発生した場合、イベント ハンドラーはブロックされ、ユーザーはフィードバックをすぐに確認できなくなります。イベント ハンドラーは、前のタスク キューに入れられるまでタスク キューに入れられます。コードが終了するまで実行は開始されません。コードに setTimeout が設定されている場合、ブラウザは適切な時間にコードをタスク キューに挿入します。この時間を 0 に設定すると、コードはすぐにキューに挿入されますが、実行されません。前のコードが実行されるまで待つ必要があります。したがって、setTimeout は実行時間を保証するものではなく、実行が間に合うかどうかは JavaScript スレッドが混雑しているかアイドル状態であるかによって決まります。

ブラウザのマルチスレッド機構とイベントループ

まず、ブラウザのカーネルがマルチスレッドであることは明らかであり、カーネルの制御下で相互に連携して同期を維持しています。少なくとも 3 つの常駐スレッドを実装する必要があります:

JavaScript エンジン スレッド

GUI レンダリング スレッド

ブラウザ イベント トリガー スレッド

JavaScript エンジンは単一です。 threaded 実行中、ブラウザでは常に JavaScript プログラムを実行するスレッドが 1 つだけです

JavaScript エンジンはイベント駆動型のシングルスレッド実行に基づいています。JS エンジンはタスク キュー内のタスクの到着を待機しています。を追加し、Processing を追加すると、ブラウザーでは常に 1 つの JS スレッドのみが JS プログラムを実行します。

GUI レンダリング スレッドは、ブラウザ インターフェイスのレンダリングを担当します。このスレッドは、インターフェイスの再描画 (再ペイント) が必要な場合、または何らかの操作によってリフローが発生した場合に実行されます。ただし、GUI レンダリング スレッドと JS エンジンは相互に排他的であることに注意してください。JS エンジンが実行されると、GUI スレッドは一時停止され、GUI の更新はキューに保存され、JS エンジンが実行されるとすぐに実行されます。アイドル。

イベント トリガー スレッド: イベントがトリガーされると、スレッドはイベントを保留キューの最後に追加し、JS エンジンによる処理を待ちます。これらのイベントは、setTimeOut などの JavaScript エンジンによって現在実行されているコード ブロック、またはマウス クリック、AJAX 非同期リクエストなどのブラウザ カーネル内の他のスレッドから発生する可能性があります。ただし、JS のシングルスレッド関係により、すべてのイベントが発生します。これらのイベントは、JS エンジンによる処理のためにキューに入れられる必要があります。 (スレッド内で同期コードが実行されていない場合、非同期コードが実行されます)

イベント ループ (イベント ループ): 非同期コードを管理するために使用され、非同期コードはスレッド プールに入れられます。

JavaScript における setTimeout の実装原理

まず、setTimeout 関数が非同期コードであることは明らかですが、実際には setTimeout は真の非同期操作ではありません

原因は次のとおりです。 JS スレッドの動作メカニズム: 非同期コードは、スレッド内で同期コードが実行されていない場合にのみ実行されます。setTimeout は非同期コードであるため、setTimeout は js がアイドル状態のときにのみ実行できます。

前述したように、コード setTimeout に a が設定されている場合、ブラウザは適切な時間にコードをタスク キューに挿入します。この時間が 0 に設定されている場合、コードはすぐにキューに挿入されますが、挿入されません。すぐに実行されますが、前のコードが実行されるまで待つ必要があります。したがって、setTimeout は実行時間を保証するものではなく、実行が間に合うかどうかは JavaScript スレッドが混雑しているかアイドル状態であるかによって決まります。

つまり、setTimeout は、タスク (実行する必要のある関数) が指定された時間後にキューに挿入されて待機することを保証するだけであり、タスクがいつ実行されるかは保証しません。 JavaScript を実行するスレッドは、キューからタスクを取り出し、アイドル状態になったときにそれを実行します。 JavaScript はこのキュー メカニズムを使用して、非同期実行のように見せかけます。

setTimeout のコードがすぐに実行されることがありますが、このコードは非同期で実行されているように感じられます。これは、JavaScript スレッドが時間のかかる操作によってブロックされないため、すぐに削除できるためです。その後、キュー内のタスクが実行されます。

例の分析

上記の理論的根拠を持った後、次の例を分析します。

======= ==== ================================

var t = true;
    
window.setTimeout(function (){
    t = false;
},1000);
    
while (t){}
    
alert('end');
実行結果: プログラムは無限ループに陥ります。

t = false は実行されないため、alert('end') は実行されません。 ### 分析: ###<p>JS是单线程的,所以会先执行 while(t){} 再 alert,但这个循环体是死循环,所以永远不会执行alert。<br></p> <p>为什么不执行 setTimeout?是因为JS的工作机制是:当线程中没有执行任何同步代码的前提下才会执行异步代码,setTimeout是异步代码,所以 setTimeout 只能等JS空闲才会执行,但死循环是永远不会空闲的,所以 setTimeout 也永远得不到执行。</p> <p>===========================================</p> <pre class="brush:php;toolbar:false">var start = new Date();      setTimeout(function(){       var end = new Date();       console.log(&quot;Time elapsed: &quot;, end - start, &quot;ms&quot;);   }, 500);          while (new Date - start &lt;= 1000){}</pre><p>运行结果:"Time elapsed: 1035 ms" (这里的1035不准确 但是一定是大于1000的)<br/> 解析:</p><p>JS是单线程 setTimeout 异步代码 其回调函数执行必须需等待主线程运行完毕</p><p>当while循环因为时间差超过 1000ms 跳出循环后,setTimeout 函数中的回调才得以执行</p><p>===========================================</p><pre class="brush:php;toolbar:false">for(var i=0;i&lt;10;i++){ setTimeout(function() { console.log(i); }, 0); }</pre><p>运行结果:输出10个10<br/> 解析:JS单线程 setTimeout 异步代码 任务队列<br/> 问:如何修改可以使上述代码输出 0123456789<br/> 自执行函数 或 使用ES6中的let关键字</p><pre class="brush:php;toolbar:false">// 自执行函数 形成闭包 记忆其被创建时的环境 for(var i=0;i&lt;10;i++){ setTimeout((function() { console.log(i); })(), 0); }</pre><h3>setTimeout(0)函数的作用</h3><p>现在我们了解了setTimeout函数执行的原理,那么它有什么作用呢?<br/>setTimeout函数增加了Javascript函数调用的灵活性,为函数执行顺序的调度提供极大便利。<br/><strong>简言之,改变顺序,这正是setTimeout(0)的作用。</strong></p><p>使用场景示例:</p><pre class="brush:php;toolbar:false">&lt;input type=&quot;text&quot; onkeydown=&quot;show(this.value)&quot;&gt;   &lt;p&gt;&lt;/p&gt;   &lt;script type=&quot;text/javascript&quot;&gt;     function show(val) {       document.getElementsByTagName('p')[0].innerHTML = val;     }   &lt;/script&gt;</pre> <p>这里绑定了 keydown 事件,意图是当用户在文本框里输入字符时,将输入的内容实时地在 <p> 中显示出来。但是实际效果并非如此,可以发现,每按下一个字符时,<p> 中只能显示出之前的内容,无法得到当前的字符。</p> <p>修改代码:</p> <pre class="brush:php;toolbar:false">  &lt;input type=&quot;text&quot; onkeydown=&quot;var self=this; setTimeout(function(){show(self.value)}, 0)&quot;&gt;     &lt;p&gt;&lt;/p&gt;     &lt;script type=&quot;text/javascript&quot;&gt;       function show(val) {         document.getElementsByTagName('p')[0].innerHTML = val;       }     &lt;/script&gt;</pre> <p>这段代码使用setTimeout(0)就可以实现需要的效果了。</p> <p>这里其实涉及2个任务,1个是将键盘输入的字符回写到输入框中,一个是获取文本框的值将其写入p中。第一个是浏览器自身的默认行为,一个是我们自己编写的代码。很显然,必须要先让浏览器将字符回写到文本框,然后我们才能获取其内容写到p中。改变顺序,这正是setTimeout(0)的作用。</p> <p>其他应用场景:有时候,加载一些广告的时候,我们用setTimeout实现异步,好让广告不会阻塞我们页面的渲染。</p> <h3>setTimeout 和 setInterval 在执行异步代码的时候有着根本的不同</h3> <p>如果一个计时器被阻塞而不能立即执行,它将延迟执行直到下一次可能执行的时间点才被执行(比期望的时间间隔要长些)</p> <p>如果setInterval回调函数的执行时间将足够长(比指定的时间间隔长),它们将连续执行并且彼此之间没有时间间隔。</p> <p>本篇文章到这里就已经全部结束了,更多其他精彩内容可以关注PHP中文网的<a href="http://www.php.cn/course/list/17.html" target="_blank">JavaScript视频教程</a>栏目!</p> <p></p>

以上がJavaScript シングルスレッドメカニズムと setTimeout 実行原理の概要 (コード付き)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はsegmentfault.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。