ホームページ > ウェブフロントエンド > jsチュートリアル > JavaScriptの実行シーケンス解析

JavaScriptの実行シーケンス解析

小云云
リリース: 2017-12-09 16:30:39
オリジナル
1802 人が閲覧しました

シングルスレッド JavaScript

まず第一に、<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>
ログイン後にコピー

しかし、JavaScript には、Ajax、setTimeout、setInterval または ES6 の Promise、async、await などの非同期プログラミングがあることもわかっています。

それでは、同期と非同期とは何ですか?

コンピュータでコマンドを実行するということは、その時点でCPUなどのリソースを使用していることを意味しており、CPUリソースを獲得したいコマンドが多く、CPUによるコマンドの実行にも時間がかかるため計算して結果を得るには、同期と非同期の概念があります。

同期とは、CPU リクエストが発行されたときに、結果が得られるまで CPU リクエストが返されないことを意味します。ただし、呼び出しが返されると、戻り値が返されます。

非同期とは、CPU リクエストが発行された後、呼び出しが直接返されるため、結果が返されないことを意味します。操作が完了した後、戻り値を取得するには一連の手段が必要です

このとき、プロセスとスレッドの概念を導入する必要があります。 プロセスとスレッド


プロセス

概念: プロセスは、データセット上で特定の独立した機能を持つプログラムの動的実行プロセスであり、オペレーティングシステムによるリソースの割り当てとスケジューリングです。独立したユニットは、アプリケーションが実行されるキャリアです。

スレッド

プロセスがCPUを順番に使用するため、プロセスの切り替えが発生しますが、現在のプログラムは比較的大きいため、切り替えのオーバーヘッドが非常に高く、CPUリソースを無駄に消費するため、これを発明しました。 . スレッドは、大規模なプロセスを複数のスレッドに分割して共同実行します。

違い

プロセスはオペレーティングシステムがリソースを割り当てる最小単位であり、スレッドはプログラム実行の最小単位です。

  • プロセスは 1 つ以上のスレッドで構成され、スレッドはプロセス内のコードの異なる実行ルートです。

  • プロセスは互いに独立していますが、プログラムは同じプロセス内のスレッド間で共有されます。メモリ空間 (コード セグメント、データ セット、ヒープなどを含む) および一部のプロセス レベルのリソース (開いているファイルやシグナルなど)。

  • スケジューリングと切り替え: スレッド コンテキストの切り替えは、プロセス コンテキストの切り替えよりもはるかに高速です。

  • 例えば

    私がナルトだったら、ラーメンを1人で10杯食べたら、一工程一糸でラーメンを食べ終えてしまいます。
  • しかし、9 つのクローンを使用して 10 杯のラーメンを一緒に食べる場合、ラーメンを食べるというタスクを完了するには 9 つのスレッドを使用する 1 つのプロセスになります。
そして、マルチプロセスとは、有名人が一楽ラーメンでラーメンを食べている間、好色な仙人が入浴中の女の子を覗いていることを意味します~~。貪欲な妖精は単一のプロセスと単一のスレッドで覗き見できます。

ブラウザのスレッド



ブラウザのカーネルは、カーネルの制御の下で、相互に連携して同期を維持します。

GUIレンダリングスレッド

  • JavaScript引擎线程

  • 事件触发线程

  • 异步http请求线程

  • EventLoop轮询的处理线程

  • 这些线程的作用:

    • UI线程用于渲染页面

    • js线程用于执行js任务

    • 浏览器事件触发线程用于控制交互,响应用户

    • http线程用于处理请求,ajax是委托给浏览器新开一个http线程

    • EventLoop处理线程用于轮询消息队列

    JavaScriptの実行シーケンス解析

    JavaScript事件循环和消息队列

    因为JavaScript是单线程的,而浏览器是多线程的,所以为了执行不同的同步异步的代码,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>拿结果。
    而每个异步任务都包含着与它相关联的信息,比如运行状态,回调函数等。
    JavaScriptの実行シーケンス解析

    由此我们可以知道,同步任务和异步任务会被分发到不同的线程去执行。
    现在我们就可以分析一下一下代码的运行结果了。

    <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>
    ログイン後にコピー

    这里我们按顺序来分析。

    第一轮
    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 までご連絡ください。
    人気のチュートリアル
    詳細>
    最新のダウンロード
    詳細>
    ウェブエフェクト
    公式サイト
    サイト素材
    フロントエンドテンプレート