> 웹 프론트엔드 > JS 튜토리얼 > JavaScript 실행 순서 분석

JavaScript 실행 순서 분석

小云云
풀어 주다: 2017-12-09 16:30:39
원래의
1786명이 탐색했습니다.

단일 스레드 JavaScript

우선, <code><span style="font-size: 14px;">JavaScript</span>JavaScript단일 스레드 해석 언어라는 것을 알아야 합니다. 이는 동시에 하나의 명령만 실행할 수 있음을 의미합니다. 단일 스레드 언어인 이유는 그 목적과 관련이 있습니다.
JavaScript는 원래 브라우저와 사용자 간의 상호 작용, 특히 양식 상호 작용을 향상시키기 위해 설계되었습니다. 나중에 Ajax 기술도 양식 상호 작용을 보다 인간적으로 만들기 위해 발명되었습니다. JavaScript는 해석된 언어이고 해석기가 브라우저에 내장되어 있으므로 이 해석기는 단일 스레드입니다.
멀티 스레드로 설계되지 않은 이유는 멀티 스레드로 인해 웹 페이지를 렌더링할 때 쉽게 교착 상태나 리소스 충돌이 발생할 수 있기 때문입니다. 그러나 브라우저 자체는 다중 스레드입니다. 예를 들어 네트워크 리소스를 로드하는 동안 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 또는 Promise, async, Wait와 같은 비동기 프로그래밍이 ES6에 있다는 것도 알고 있습니다.

그럼 동기식과 비동기식은 무엇일까요?

컴퓨터에서 명령을 실행한다는 것은 이때 CPU 등의 자원을 사용하고 있다는 뜻이므로 CPU 자원을 얻으려는 명령이 많고 명령을 실행하는 CPU도 시간이 걸리기 때문입니다. 결과를 계산하고 얻으려면 동기화와 비동기성의 개념이 필요합니다.

동기화는 CPU 요청이 발행되면 결과를 얻을 때까지 CPU 요청이 반환되지 않음을 의미합니다. 그러나 일단 호출이 반환되면 반환 값을 얻게 됩니다.

비동기란 CPU 요청이 실행된 후 호출이 직접 반환되므로 결과가 반환되지 않음을 의미합니다. 작업이 완료된 후 반환 값을 얻기 위해서는 일련의 수단이 필요합니다

이때 프로세스와 스레드의 개념을 도입해야 합니다. Processes and Threads


Process

개념: 프로세스는 데이터 세트에 대한 특정 독립적 기능을 갖춘 프로그램의 동적 실행 프로세스입니다. 이는 운영 체제에 의한 리소스 할당 및 예약입니다. 독립 장치는 애플리케이션이 실행되는 캐리어입니다.

Threads

프로세스가 CPU를 차례로 사용하기 때문에 프로세스 스위칭이 있습니다. 그러나 현재 프로그램이 상대적으로 크기 때문에 스위칭 오버헤드가 매우 높고 CPU 리소스를 낭비하게 되므로 이를 고안했습니다. 스레드는 공동 실행을 위해 대규모 프로세스를 여러 스레드로 나눕니다.

Difference

프로세스는 운영체제가 자원을 할당하는 최소 단위이고, 스레드는 프로그램 실행을 위한 최소 단위입니다.

  • 프로세스는 하나 이상의 스레드로 구성되며 스레드는 프로세스 내에서 서로 다른 코드 실행 경로입니다.

  • 프로세스는 서로 독립적이지만 프로그램은 동일한 프로세스 아래의 스레드 간에 공유됩니다. 메모리 공간(코드 세그먼트, 데이터 세트, 힙 등 포함) 및 일부 프로세스 수준 리소스(예: 열린 파일 및 신호).

  • 예약 및 전환: 스레드 컨텍스트 전환은 프로세스 컨텍스트 전환보다 훨씬 빠릅니다.

  • 예를 들어

    내가 나루토라면 라면 10그릇을 혼자 먹으면 라면 한가닥, 한가닥이면 다 먹습니다.
  • 하지만 9개의 클론을 사용하여 라면 10그릇을 먹는다면 9개의 스레드를 사용하여 라면을 먹는 작업을 완료하는 하나의 프로세스입니다.
그리고 멀티프로세스는 연예인이 이치라쿠 라면에서 라면을 먹고 있는 동안, 음란한 불멸자가 목욕하는 소녀를 엿보고 있다는 뜻입니다~~. 음탕한 요정은 단일 프로세스와 단일 스레드로 엿볼 수 있습니다!

브라우저의 스레드



브라우저의 커널은 커널의 제어에 따라 서로 협력하여 동기화를 유지합니다.

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...")
    로그인 후 복사

    相关推荐:

    자바스크립트 코드 실행 순서 상세 설명

    자바 클래스의 로딩 순서 실행 결과 상세 소개

    외부 프로그램을 실행하는 PHP 구현 코드 상세 설명

    위 내용은 JavaScript 실행 순서 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

    관련 라벨:
    원천:php.cn
    본 웹사이트의 성명
    본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
    인기 튜토리얼
    더>
    최신 다운로드
    더>
    웹 효과
    웹사이트 소스 코드
    웹사이트 자료
    프론트엔드 템플릿