JavaScriptの実行シーケンス解析

小云云
小云云オリジナル
2017-12-09 16:30:391803ブラウズ

シングルスレッド JavaScript

まず第一に、<code><span style="font-size: 14px;">JavaScript</span>JavaScriptシングルスレッド インタプリタ言語であることを知っておく必要があります。これは、同時に 1 つのコマンドしか実行できないことを意味します。これがシングルスレッド言語である理由は、その目的に関係しています。
JavaScript は元々、ブラウザとユーザー間の対話、特にフォームの対話を強化するために設計されました。その後、フォームの対話をより人間味のあるものにするために Ajax テクノロジーも発明されました。 JavaScript はインタープリター言語であり、インタープリターはブラウザーに組み込まれているため、このインタープリターはシングルスレッドです。
マルチスレッド向けに設計されていない理由は、Web ページをレンダリングするときにマルチスレッドではデッドロックやリソースの競合が発生しやすいためです。ただし、ブラウザ自体はマルチスレッドです。たとえば、ネットワーク リソースを読み込むときに JavaScript を解釈して実行します。

なぜ JavaScript はマルチスレッドをサポートしないのですか?

fc5d08fd379408d84610ae26cb4bf4f8{console.log("我才是第一");},0);
console.log("我是第一");

  1. 因为setTimeout是异步的事件,所以主线程把它调入Event Loop线程进行注册。

  2. 主线程继续执行<span style="font-size: 14px;">console.log("我是第一");</span>

  3. 主线程执行完毕,从Event Loop 线程读取回调函数。再执行<span style="font-size: 14px;">console.log("我才是第一");</span>;

setTimeout 和 setInterval

<span style="font-size: 14px;">setTimeout</span>

这里值得一提的是,<span style="font-size: 14px;">setTimeout(callback,0)</span>指的是主线程中的同步任务运行完了之后立刻由Event Loop 线程调入主线程。
而计时是在调入Event Loop线程注册时开始的,此时
<span style="font-size: 14px;">setTimeout的回调函数执行时间</span>与主线程运行结束的时间相关。
关于setTimeout要补充的是,即便主线程为空,0毫秒实际上也是达不到的。根据HTML的标准,最低是4毫秒。

setInterval

需要注意的是,此函数是每隔一段时间将回调函数放入Event Loop线程。
一旦setInterval的回调函数fn执行时间超过了延迟时间ms,那么就完全看不出来有时间间隔了

<span style="font-size: 14px;">micro-task(微任务)</span><span style="font-size: 14px;">macro-task(宏任务)</span>

<span style="font-size: 14px;">Event Loop线程</span>中包含任务队列(用来对不同优先级的异步事件进行排序),而任务队列又分为<span style="font-size: 14px;">macro-task(宏任务)</span><span style="font-size: 14px;">micro-task(微任务)</span>,在最新标准中,它们被分别称为<span style="font-size: 14px;">task</span><span style="font-size: 14px;">jobs</span>

  • macro-task大概包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。

  • micro-task大概包括: process.nextTick, Promise, Object.observe(已废弃), MutationObserver(html5新特性)

  • setTimeout/Promise等我们称之为<span style="font-size: 14px;">任务源</span>。而进入任务队列的是他们指定的具体执行任务(回调函数)。

来自不同的任务源的任务会进入到不同的任务队列中,而不同的任务队列执行过程如下:
执行过程如下:
JavaScript引擎首先从
<span style="font-size: 14px;">macro-task</span>中取出第一个任务,
执行完毕后,将
<span style="font-size: 14px;">micro-task</span>中的所有任务取出,按顺序全部执行;
然后再从
<span style="font-size: 14px;">macro-task</span>中取下一个,
执行完毕后,再次将
<span style="font-size: 14px;">micro-task</span>中的全部取出;
循环往复,直到两个队列中的任务都取完。

举个大例子

<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>

这里我们按顺序来分析。

第一轮
  1. 整体script代码作为一个宏任务进入主线程,运行<span style="font-size: 14px;">console.log("start");</span>

  2. 然后遇到<span style="font-size: 14px;">Promises</span>直接运行<span style="font-size: 14px;">console.log("promise start..")</span>

  3. 然后遇到<span style="font-size: 14px;">promise.then</span>,存入到<span style="font-size: 14px;">micro-task队列</span>中。

  4. 然后遇到<span style="font-size: 14px;">setTimeout</span>,存入到<span style="font-size: 14px;">macro-task队列</span>中。

  5. 于然后运行<span style="font-size: 14px;">console.log("test end...")</span>;

  6. 在这一轮中,宏任务运行结束,运行micro-task队列中的 <span style="font-size: 14px;">promise.then</span>,输出<span style="font-size: 14px;">promise</span>

第二轮
  1. 取出<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...")

相关推荐:

JavaScriptのコード実行順序を詳しく解説

Javaのクラスのロード順序実行結果を詳しく紹介

外部プログラムを実行するPHPの実装コードを詳しく解説

以上がJavaScriptの実行シーケンス解析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。