まず第一に、<code><span style="font-size: 14px;">JavaScript</span>
JavaScript は シングルスレッド インタプリタ言語であることを知っておく必要があります。これは、同時に 1 つのコマンドしか実行できないことを意味します。これがシングルスレッド言語である理由は、その目的に関係しています。
JavaScript は元々、ブラウザとユーザー間の対話、特にフォームの対話を強化するために設計されました。その後、フォームの対話をより人間味のあるものにするために Ajax テクノロジーも発明されました。 JavaScript はインタープリター言語であり、インタープリターはブラウザーに組み込まれているため、このインタープリターはシングルスレッドです。
マルチスレッド向けに設計されていない理由は、Web ページをレンダリングするときにマルチスレッドではデッドロックやリソースの競合が発生しやすいためです。ただし、ブラウザ自体はマルチスレッドです。たとえば、ネットワーク リソースを読み込むときに JavaScript を解釈して実行します。
なぜ JavaScript はマルチスレッドをサポートしないのですか?
コマンドは順序付けする必要があり、通常、これらのコマンドは上から下に実行されます (インタプリタは先頭から開始するため)。ファイル)。たとえば、次のコードは順番に実行されます。
<span style="font-size: 14px;">console.log("1");<br/>console.log("2");<br/>console.log("3");<br/>//1<br/>//2<br/>//3<br/></span>
それでは、同期と非同期とは何ですか?
コンピュータでコマンドを実行するということは、その時点でCPUなどのリソースを使用していることを意味しており、CPUリソースを獲得したいコマンドが多く、CPUによるコマンドの実行にも時間がかかるため計算して結果を得るには、同期と非同期の概念があります。
非同期とは、CPU リクエストが発行された後、呼び出しが直接返されるため、結果が返されないことを意味します。操作が完了した後、戻り値を取得するには一連の手段が必要です
このとき、プロセスとスレッドの概念を導入する必要があります。 プロセスとスレッド
プロセス
プロセスがCPUを順番に使用するため、プロセスの切り替えが発生しますが、現在のプログラムは比較的大きいため、切り替えのオーバーヘッドが非常に高く、CPUリソースを無駄に消費するため、これを発明しました。 . スレッドは、大規模なプロセスを複数のスレッドに分割して共同実行します。
プロセスはオペレーティングシステムがリソースを割り当てる最小単位であり、スレッドはプログラム実行の最小単位です。
私がナルトだったら、ラーメンを1人で10杯食べたら、一工程一糸でラーメンを食べ終えてしまいます。
ブラウザのカーネルは、カーネルの制御の下で、相互に連携して同期を維持します。
JavaScript引擎线程 事件触发线程 异步http请求线程 EventLoop轮询的处理线程 这些线程的作用: UI线程用于渲染页面 js线程用于执行js任务 浏览器事件触发线程用于控制交互,响应用户 http线程用于处理请求,ajax是委托给浏览器新开一个http线程 EventLoop处理线程用于轮询消息队列 因为JavaScript是单线程的,而浏览器是多线程的,所以为了执行不同的同步异步的代码,JavaScript运行的环境采用里事件循环和消息队列来达到目的。 这里我们按顺序来分析。 整体script代码作为一个宏任务进入主线程,运行 然后遇到 然后遇到 然后遇到 于然后运行 在这一轮中,宏任务运行结束,运行micro-task队列中的 取出 输出的顺序就是 相关推荐: 以上がJavaScriptの実行シーケンス解析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。JavaScript事件循环和消息队列
每个线程的任务执行顺序都是FIFO(先进先出)
在JavaScript运行的环境中,有一个负责程序本身的运行,作为主线程;另一个负责主线程与其他线程的通信,被称为<span style="font-size: 14px;">Event Loop 线程</span>
。
每当主线程遇到异步的任务,把他们移入到<span style="font-size: 14px;">Event Loop 线程</span>
,然后主线程继续运行,等到主线程完全运行完之后,再去<span style="font-size: 14px;">Event Loop 线程</span>
拿结果。
而每个异步任务都包含着与它相关联的信息,比如运行状态,回调函数等。
由此我们可以知道,同步任务和异步任务会被分发到不同的线程去执行。
现在我们就可以分析一下一下代码的运行结果了。<span style="font-size: 14px;">setTimeout(()=>{console.log("我才是第一");},0);<br>console.log("我是第一");<br></span></p>
<ol class=" list-paddingleft-2">
<li><p><span style="font-size: 14px;">因为setTimeout是异步的事件,所以主线程把它调入Event Loop线程进行注册。</span></p></li>
<li><p><span style="font-size: 14px;">主线程继续执行</span><code><span style="font-size: 14px;">console.log("我是第一");</span></code></p></li>
<li><p><span style="font-size: 14px;">主线程执行完毕,从Event Loop 线程读取回调函数。再执行</span><code><span style="font-size: 14px;">console.log("我才是第一");</span></code><span style="font-size: 14px;">;</span></p></li>
</ol>
<h3><span style="font-size: 14px;">setTimeout 和 setInterval</span></h3>
<h4><span style="font-size: 14px;">setTimeout</span></h4>
<p><span style="font-size: 14px;">这里值得一提的是,</span><code><span style="font-size: 14px;">setTimeout(callback,0)</span></code><span style="font-size: 14px;">指的是主线程中的同步任务运行完了之后立刻由Event Loop 线程调入主线程。<br>而计时是在调入Event Loop线程注册时开始的,此时</span><code><span style="font-size: 14px;">setTimeout的回调函数执行时间</span></code><span style="font-size: 14px;">与主线程运行结束的时间相关。<br>关于setTimeout要补充的是,即便主线程为空,0毫秒实际上也是达不到的。根据HTML的标准,最低是4毫秒。</span></p>
<h4><span style="font-size: 14px;">setInterval</span></h4>
<p><span style="font-size: 14px;">需要注意的是,此函数是每隔一段时间将回调函数放入Event Loop线程。<br><strong>一旦setInterval的回调函数fn执行时间超过了延迟时间ms,那么就完全看不出来有时间间隔了</strong></span></p>
<h4>
<code><span style="font-size: 14px;">micro-task(微任务)</span></code><span style="font-size: 14px;"> 与 </span><code><span style="font-size: 14px;">macro-task(宏任务)</span></code>
</h4>
<p><code><span style="font-size: 14px;">Event Loop线程</span></code><span style="font-size: 14px;">中包含任务队列(用来对不同优先级的异步事件进行排序),而任务队列又分为</span><code><span style="font-size: 14px;">macro-task(宏任务)</span></code><span style="font-size: 14px;">与</span><code><span style="font-size: 14px;">micro-task(微任务)</span></code><span style="font-size: 14px;">,在最新标准中,它们被分别称为</span><code><span style="font-size: 14px;">task</span></code><span style="font-size: 14px;">与</span><code><span style="font-size: 14px;">jobs</span></code><span style="font-size: 14px;">。</span></p>
<ul class=" list-paddingleft-2">
<li><p><span style="font-size: 14px;">macro-task大概包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。</span></p></li>
<li><p><span style="font-size: 14px;">micro-task大概包括: process.nextTick, Promise, Object.observe(已废弃), MutationObserver(html5新特性)</span></p></li>
<li><p><span style="font-size: 14px;">setTimeout/Promise等我们称之为</span><code><span style="font-size: 14px;">任务源</span></code><span style="font-size: 14px;">。而进入任务队列的是他们指定的具体执行任务(回调函数)。</span></p></li>
</ul>
<p><span style="font-size: 14px;">来自不同的任务源的任务会进入到不同的任务队列中,而不同的任务队列执行过程如下:<br>执行过程如下:<br>JavaScript引擎首先从</span><code><span style="font-size: 14px;">macro-task</span></code><span style="font-size: 14px;">中取出第一个任务,<br>执行完毕后,将</span><code><span style="font-size: 14px;">micro-task</span></code><span style="font-size: 14px;">中的所有任务取出,按顺序全部执行;<br>然后再从</span><code><span style="font-size: 14px;">macro-task</span></code><span style="font-size: 14px;">中取下一个,<br>执行完毕后,再次将</span><code><span style="font-size: 14px;">micro-task</span></code><span style="font-size: 14px;">中的全部取出;<br>循环往复,直到两个队列中的任务都取完。</span></p>
<h4><span style="font-size: 14px;">举个大例子</span></h4>
<pre class="brush:php;toolbar:false"><span style="font-size: 14px;">console.log("start");<br>var promise = new Promise((resolve) => {<br> console.log("promise start..");<br> resolve("promise");<br>}); //3<br>promise.then((val) => console.log(val));<br>setTimeout(()=>{console.log("setTime1")},0);<br>console.log("test end...")<br></span>
第一轮
<span style="font-size: 14px;">console.log("start");</span>
。<span style="font-size: 14px;">Promises</span>
直接运行<span style="font-size: 14px;">console.log("promise start..")</span>
。<span style="font-size: 14px;">promise.then</span>
,存入到<span style="font-size: 14px;">micro-task队列</span>
中。<span style="font-size: 14px;">setTimeout</span>
,存入到<span style="font-size: 14px;">macro-task队列</span>
中。<span style="font-size: 14px;">console.log("test end...")</span>
;<span style="font-size: 14px;">promise.then</span>
,输出<span style="font-size: 14px;">promise</span>
第二轮
<span style="font-size: 14px;">macro-task队列</span>
中的<span style="font-size: 14px;">setTimeout</span>
,运行<span style="font-size: 14px;">console.log("setTime1");</span>
结果
<span style="font-size: 14px;">// start<br>// promise start<br>// test end...<br>// promise<br>//setTime1<br></span>
留一个案例你们去分析
async function testSometing() {
console.log("执行testSometing");
return "testSometing";
}
async function testAsync() {
console.log("执行testAsync");
return Promise.resolve("hello async");
}
async function test() {
console.log("test start...");
const v1 = await testSometing();
console.log(v1);
const v2 = await testAsync();
console.log(v2);
console.log(v1, v2);
}
test();
var promise = new Promise((resolve) => {
console.log("promise start..");
resolve("promise");
}); //3
promise.then((val) => console.log(val));
setTimeout(()=>{console.log("setTime1")},3000);
console.log("test end...")