脚本宝典收集整理的这篇文章主要介绍了

JS单线程与setTimeout执行原理

脚本宝典小编觉得挺不错的,现在分享给大家,也给大家做个参考,希望能帮助你少写一行代码,多一份安全和惬意。
<p><code></p> <h3 id="articleHeader0">Javascript 引擎单线程机制</h3> <ul> <li>首先明确,JavaScript引擎是单线程机制。</li> <li>JavaScript 是单线程执行的,无法同时执行多段代码。当某一段代码正在执行的时候,所有后续的任务都必须等待,形成一个任务队列。一旦当前任务执行完毕,再从队列中取出下一个任务,这也常被称为 “阻塞式执行”。</li> <li> <p>可以理解为:只有在JS线程中没有任何同步代码要执行的前提下才会执行异步代码</p> </li> <li>所以一次鼠标点击,或是计时器到达时间点,或是 Ajax 请求完成触发了回调函数,这些事件处理程序或回调函数都不会立即运行,而是立即排队,一旦线程有空闲就 执行。假如当前 JavaScript 线程正在执行一段很耗时的代码,此时发生了一次鼠标点击,那么事件处理程序就被阻塞,用户也无法立即看到反馈,事件处理程序会被放入任务队列,直到前面的代码结束以后才会开始执行。如果代码中设定了一个 <a href="http://www.js-code.com/tag/setTimeout" title="setTimeout" target="_blank">setTimeout</a>,那么浏览器便会在合适的时间,将代码插入任务队列,如果这个时间设为 0,就代表立即插入队列,但不是立即执行,仍然要等待前面代码执行完毕。所以 <a href="http://www.js-code.com/tag/setTimeout" title="setTimeout" target="_blank">setTimeout</a> 并不能保证执行的时间,是否及时执行取决于 JavaScript 线程是拥挤还是空闲。</li> </ul> <h3 id="articleHeader1">浏览器的多线程机制与事件循环(<a href="http://www.js-code.com/tag/event" title="event" target="_blank">event</a> loop)</h3> <ul> <li> <p>首先明确,浏览器的内核是多线程的,它们在内核制控下相互配合以保持同步,一个浏览器至少实现三个常驻线程:</p> <ul> <li><a href="http://www.js-code.com/tag/java" title="java" target="_blank">java</a>script 引擎线程</li> <li>GUI 渲染线程</li> <li>浏览器事件触发线程</li> </ul> </li> <li> <p>JavaScript 引擎是单线程运行的,浏览器无论在什么时候都只且只有一个线程在运行JavaScript程序</p> <ul> <li><a href="http://www.js-code.com/tag/java" title="java" target="_blank">java</a>script 引擎是基于事件驱动单线程执行的,JS引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个JS线程在运行JS程序。</li> </ul> </li> <li>GUI渲染线程负责渲染浏览器界面,当界面需要重绘(Repa<a href="http://www.js-code.com/tag/in" title="in" target="_blank">in</a>t)或由于某种操作引发回流(reflow)时,该线程就会执行。但需要注意 GUI渲染线程与JS引擎是互斥的,当JS引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。</li> <li>事件触发线程,当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理。这些事件可来自 JavaScript 引擎当前执行的代码块如 setTimeOut,也可来自浏览器内核的其他线程如鼠标点击、<a href="http://www.js-code.com/tag/ajax" title="AJAX" target="_blank">AJAX</a> 异步请求等,但由于JS的单线程关系所有这些事件都得排队等待JS引擎处理。(当线程中没有执行任何同步代码的前提下才会执行异步代码)</li> <li>事件循环(<a href="http://www.js-code.com/tag/event" title="event" target="_blank">event</a> loop): 是用来管理我们的异步代码的,它会把它们放在一个线程池当中</li> </ul> <h3 id="articleHeader2">JavaScript中<a href="http://www.js-code.com/tag/setTimeout" title="浏览关于“setTimeout”的文章" target="_blank" class="tag_link">setTimeout</a>的实现原理</h3> <ul> <li>首先明确,setTimeout函数是异步代码,但其实setTimeout并不是真正的异步操作</li> <li>由于JS线程的工作机制:当线程中没有执行任何同步代码的前提下才会执行异步代码,setTimeout是异步代码,所以setTimeout只能等js空闲才会执行</li> <li>前面提到过,如果代码中设定了一个 setTimeout,那么浏览器便会在合适的时间,将代码插入任务队列,如果这个时间设为 0,就代表立即插入队列,但不是立即执行,仍然要等待前面代码执行完毕。所以 setTimeout 并不能保证执行的时间,是否及时执行取决于 JavaScript 线程是拥挤还是空闲。</li> <li>也就是说setTimeout只能保证在指定的时间过后将任务(需要执行的函数)插入队列等候,并不保证这个任务在什么时候执行。执行<a href="http://www.js-code.com/tag/java" title="浏览关于“java”的文章" target="_blank" class="tag_link">java</a>script的线程会在空闲的时候,自行从队列中取出任务然后执行它。javascript 通过这种队列机制,给我们制造一个异步执行的假象。</li> <li>有时setTimeout中的代码会很快得到执行,我们会感觉这段代码是在异步执行,这是因为 javascript 线程并没有因为什么耗时操作而阻塞,所以可以很快地取出排队队列中的任务然后执行它。</li> </ul> <h3 id="articleHeader3">实例分析</h3> <p>在具备了上述理论基础之后,我们对以下几个实例进行分析:</p> <ol> <li> <p>===========================================</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="var t = true; window.setTimeout(function (){ t = false; },1000); while (t){} alert('end');" title="" data-original-title="复制"></span> </div> </p></div> <pre class="hljs javascript"><code><span class="hljs-keyword"><a href="http://www.js-code.com/tag/var" title="浏览关于“var”的文章" target="_blank" class="tag_link">var</a></span> t = <span class="hljs-literal"><a href="http://www.js-code.com/tag/true" title="浏览关于“true”的文章" target="_blank" class="tag_link">true</a></span>; <span class="hljs-built_in"><a href="http://www.js-code.com/tag/window" title="浏览关于“window”的文章" target="_blank" class="tag_link">window</a></span>.setTimeout(<span class="hljs-function"><span class="hljs-keyword"><a href="http://www.js-code.com/tag/function" title="浏览关于“function”的文章" target="_blank" class="tag_link">function</a></span> (<span class="hljs-params"></span>)</span>{ t = <span class="hljs-literal"><a href="http://www.js-code.com/tag/false" title="浏览关于“false”的文章" target="_blank" class="tag_link">false</a></span>; },<span class="hljs-number">1000</span>); <span class="hljs-keyword"><a href="http://www.js-code.com/tag/while" title="浏览关于“while”的文章" target="_blank" class="tag_link">while</a></span> (t){} <a href="http://www.js-code.com/tag/alert" title="浏览关于“alert”的文章" target="_blank" class="tag_link">alert</a>(<span class="hljs-string">'end'</span>);</code></pre> <p>运行结果:程序陷入死循环,<code>t = <a href="http://www.js-code.com/tag/false" title="false" target="_blank">false</a></code> 得不到执行,因此 <code><a href="http://www.js-code.com/tag/alert" title="alert" target="_blank">alert</a>('end')</code> 不会执行。<br /> 解析:</p> <ul> <li>JS是单线程的,所以会先执行 <a href="http://www.js-code.com/tag/while" title="while" target="_blank">while</a>(t){} 再 <a href="http://www.js-code.com/tag/alert" title="alert" target="_blank">alert</a>,但这个循环体是死循环,所以永远不会执行alert。</li> <li>为什么不执行 setTimeout?是因为JS的工作机制是:当线程中没有执行任何同步代码的前提下才会执行异步代码,setTimeout是异步代码,所以 setTimeout 只能等JS空闲才会执行,但死循环是永远不会空闲的,所以 setTimeout 也永远得不到执行。</li> </ul> </li> <li> <p>===========================================</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="<a href="http://www.js-code.com/tag/button" title="button" target="_blank">button</a>" <a href="http://www.js-code.com/tag/class" title="class" target="_blank">class</a>="copyCode code-tool" data-toggle="tooltip" data-placement="<a href="http://www.js-code.com/tag/top" title="top" target="_blank">top</a>" data-clipboard-<a href="http://www.js-code.com/tag/text" title="text" target="_blank">text</a>="<a href="http://www.js-code.com/tag/var" title="var" target="_blank">var</a> start = <a href="http://www.js-code.com/tag/new" title="new" target="_blank">new</a> <a href="http://www.js-code.com/tag/Date" title="Date" target="_blank">Date</a>();</p> <p>setTimeout(<a href="http://www.js-code.com/tag/function" title="function" target="_blank">function</a>(){<br /> <a href="http://www.js-code.com/tag/var" title="var" target="_blank">var</a> end = <a href="http://www.js-code.com/tag/new" title="new" target="_blank">new</a> <a href="http://www.js-code.com/tag/Date" title="Date" target="_blank">Date</a>();<br /> console.log(&quot;Time elapsed: &quot;, end - start, &quot;ms&quot;);<br /> }, 500); </p> <p><a href="http://www.js-code.com/tag/while" title="while" target="_blank">while</a> (<a href="http://www.js-code.com/tag/new" title="浏览关于“new”的文章" target="_blank" class="tag_link">new</a> <a href="http://www.js-code.com/tag/Date" title="浏览关于“Date”的文章" target="_blank" class="tag_link">Date</a> - start <= 1000){}" title="" data-original-title="复制"></span> </div> </p></div> <pre class="hljs javascript"><code><span class="hljs-keyword">var</span> start = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(); setTimeout(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{ <span class="hljs-keyword">var</span> end = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(); <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Time elapsed: "</span>, end - start, <span class="hljs-string">"ms"</span>); }, <span class="hljs-number">500</span>); <span class="hljs-keyword">while</span> (<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span> - start &lt;= <span class="hljs-number">1000</span>){}</code></pre> <p>运行结果:"Time elapsed: 1035 ms" (这里的1035不准确 但是一定是大于1000的)<br /> 解析:</p> <ul> <li>JS是单线程 setTimeout 异步代码 其回调函数执行必须需等待主线程运行完毕</li> <li>当while循环因为时间差超过 1000ms 跳出循环后,setTimeout 函数中的回调才得以执行</li> </ul> </li> <li> <p>===========================================</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="<a href="http://www.js-code.com/tag/button" title="button" target="_blank">button</a>" <a href="http://www.js-code.com/tag/class" title="class" target="_blank">class</a>="copyCode code-tool" data-toggle="tooltip" data-placement="<a href="http://www.js-code.com/tag/top" title="top" target="_blank">top</a>" data-clipboard-<a href="http://www.js-code.com/tag/text" title="text" target="_blank">text</a>="<a href="http://www.js-code.com/tag/for" title="for" target="_blank">for</a>(var i=0;i<10;i++){ setTimeout(function() { console.log(i); }, 0); } " title="" data-original-title="复制"></span> </div> </p></div> <pre class="hljs lisp"><code><a href="http://www.js-code.com/tag/for" title="浏览关于“for”的文章" target="_blank" class="tag_link">for</a>(<span class="hljs-name">var</span> i=0<span class="hljs-comment">;i&lt;10;i++){</span> setTimeout(<span class="hljs-name">function</span>() { console.log(<span class="hljs-name">i</span>)<span class="hljs-comment">;</span> }, <span class="hljs-number">0</span>)<span class="hljs-comment">;</span> } </code></pre> <p>运行结果:输出10个10<br /> 解析:JS单线程 setTimeout 异步代码 任务队列<br /> 问:如何修改可以使上述代码输出 0123456789<br /> 自执行函数 或 使用ES6中的<a href="http://www.js-code.com/tag/let" title="let" target="_blank">let</a>关键字</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="<a href="http://www.js-code.com/tag/button" title="浏览关于“button”的文章" target="_blank" class="tag_link">button</a>" class="copyCode code-tool" data-toggle="tooltip" data-placement="<a href="http://www.js-code.com/tag/top" title="浏览关于“top”的文章" target="_blank" class="tag_link">top</a>" data-clipboard-<a href="http://www.js-code.com/tag/text" title="浏览关于“text”的文章" target="_blank" class="tag_link">text</a>="// 自执行函数 形成闭包 记忆其被创建时的环境<br /> <a href="http://www.js-code.com/tag/for" title="for" target="_blank">for</a>(var i=0;i<10;i++){ setTimeout((function() { console.log(i); })(), 0); }" title="" data-original-title="复制"></span> </div> </p></div> <pre class="hljs javascript"><code><span class="hljs-comment">// 自执行函数 形成闭包 记忆其被创建时的环境</span> <span class="hljs-keyword">for</span>(<span class="hljs-keyword">var</span> i=<span class="hljs-number">0</span>;i&lt;<span class="hljs-number">10</span>;i++){ setTimeout((<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-built_in">console</span>.log(i); })(), <span class="hljs-number">0</span>); }</code></pre> </li> </ol> <h3 id="articleHeader4">setTimeout(0)函数的作用</h3> <p>现在我们了解了setTimeout函数执行的原理,那么它有什么作用呢?<br />setTimeout函数增加了Javascript函数调用的灵活性,为函数执行顺序的调度提供极大便利。<br /><strong>简言之,改变顺序,这正是setTimeout(0)的作用。</strong></p> <p>使用场景示例:</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="<input type=&quot;text&quot; onkeydown=&quot;show(this.value)&quot;> </p> <div></div> <p><script type=&quot;text/javascript&quot;> <a href="http://www.js-code.com/tag/function" title="function" target="_blank">function</a> show(val) { <a href="http://www.js-code.com/tag/do" title="do" target="_blank">do</a>cument.getElementsByTagName('<a href="http://www.js-code.com/tag/div" title="div" target="_blank">div</a>')[0].<a href="http://www.js-code.com/tag/in" title="in" target="_blank">in</a>ner<a href="http://www.js-code.com/tag/html" title="HTML" target="_blank">HTML</a> = val; } </script> " title="" data-original-title="复制"></span> </div> </p></div> <pre class="hljs xml"><code><span class="hljs-tag">&lt;<span class="hljs-name"><a href="http://www.js-code.com/tag/in" title="浏览关于“in”的文章" target="_blank" class="tag_link">in</a>put</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr"><a href="http://www.js-code.com/tag/onkeydown" title="浏览关于“onkeydown”的文章" target="_blank" class="tag_link">onkeydown</a></span>=<span class="hljs-string">"show(<a href="http://www.js-code.com/tag/this" title="浏览关于“this”的文章" target="_blank" class="tag_link">this</a>.value)"</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name"><a href="http://www.js-code.com/tag/div" title="浏览关于“div”的文章" target="_blank" class="tag_link">div</a></span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/javascript"</span>&gt;</span><span class="javascript"> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">show</span>(<span class="hljs-params">val</span>) </span>{ <span class="hljs-built_in"><a href="http://www.js-code.com/tag/document" title="浏览关于“document”的文章" target="_blank" class="tag_link">document</a></span>.getElementsByTagName(<span class="hljs-string">'div'</span>)[<span class="hljs-number">0</span>].inner<a href="http://www.js-code.com/tag/html" title="浏览关于“HTML”的文章" target="_blank" class="tag_link">HTML</a> = val; } </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span> </code></pre> <p>这里绑定了 key<a href="http://www.js-code.com/tag/do" title="do" target="_blank">do</a>wn 事件,意图是当用户在文本框里输入字符时,将输入的内容实时地在 &lt;<a href="http://www.js-code.com/tag/div" title="div" target="_blank">div</a>&gt; 中显示出来。但是实际效果并非如此,可以发现,每按下一个字符时,&lt;div&gt; 中只能显示出之前的内容,无法得到当前的字符。</p> <p>修改代码:</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text=" <input type=&quot;text&quot; onkeydown=&quot;var self=this; setTimeout(function(){show(self.value)}, 0)&quot;> </p> <div></div> <p> <script type=&quot;text/javascript&quot;> <a href="http://www.js-code.com/tag/function" title="function" target="_blank">function</a> show(val) { <a href="http://www.js-code.com/tag/document" title="document" target="_blank">document</a>.getElementsByTagName('div')[0].inner<a href="http://www.js-code.com/tag/html" title="HTML" target="_blank">HTML</a> = val; } </script>" title="" data-original-title="复制"></span> </div> </p></div> <pre class="hljs xml"><code> <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">onkey<a href="http://www.js-code.com/tag/do" title="浏览关于“do”的文章" target="_blank" class="tag_link">do</a>wn</span>=<span class="hljs-string">"var <a href="http://www.js-code.com/tag/self" title="浏览关于“self”的文章" target="_blank" class="tag_link">self</a>=this; setTimeout(function(){show(self.value)}, 0)"</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/javascript"</span>&gt;</span><span class="javascript"> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">show</span>(<span class="hljs-params">val</span>) </span>{ <span class="hljs-built_in">document</span>.getElementsByTagName(<span class="hljs-string">'div'</span>)[<span class="hljs-number">0</span>].innerHTML = val; } </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></code></pre> <p>这段代码使用setTimeout(0)就可以实现需要的效果了。</p> <p>这里其实涉及2个任务,1个是将键盘输入的字符回写到输入框中,一个是获取文本框的值将其写入div中。第一个是浏览器自身的默认行为,一个是我们自己编写的代码。很显然,必须要先让浏览器将字符回写到文本框,然后我们才能获取其内容写到div中。改变顺序,这正是setTimeout(0)的作用。</p> <p>其他应用场景:有时候,加载一些广告的时候,我们用setTimeout实现异步,好让广告不会阻塞我们页面的渲染。</p> <h3 id="articleHeader5">setTimeout 和 <a href="http://www.js-code.com/tag/setInterval" title="setInterval" target="_blank">setInterval</a> 在执行异步代码的时候有着根本的不同</h3> <ul> <li>如果一个计时器被阻塞而不能立即执行,它将延迟执行直到下一次可能执行的时间点才被执行(比期望的时间间隔要长些)</li> <li>如果<a href="http://www.js-code.com/tag/setInterval" title="setInterval" target="_blank">setInterval</a>回调函数的执行时间将足够长(比指定的时间间隔长),它们将连续执行并且彼此之间没有时间间隔。</li> </ul> <p></code></p>

总结

以上是脚本宝典为你收集整理的

JS单线程与setTimeout执行原理

全部内容,希望文章能够帮你解决

JS单线程与setTimeout执行原理

所遇到的程序开发问题,欢迎加入QQ群277859234一起讨论学习。如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典网站推荐给程序员好友。 本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。

80%的人都看过