亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

JavaScript執(zhí)行順序分析

chnmagnus / 3037人閱讀

摘要:每個線程的任務(wù)執(zhí)行順序都是先進(jìn)先出在運行的環(huán)境中,有一個負(fù)責(zé)程序本身的運行,作為主線程另一個負(fù)責(zé)主線程與其他線程的通信,被稱為線程。主線程繼續(xù)執(zhí)行我是第一主線程執(zhí)行完畢,從線程讀取回調(diào)函數(shù)。

前言

上星期面試被問到了事件執(zhí)行順序的問題,想起來之前看《深入淺出Node.js》時看到這一章就忽略了,這次來分析一下JavaScript的事件執(zhí)行順序。廢話少說,正題開始。

單線程JavaScript

首先我們要知道JavaScript是一門單線程解釋型語言。這就意味著在同一個時間下,我們只能執(zhí)行一條命令。之所以它是一門單線程語言,和它的用途有關(guān)。
JavaScript設(shè)計出來的初衷是為了增強(qiáng)瀏覽器與用戶的交互,尤其是表單的交互,而之后的Ajax技術(shù)也是為了使表單的交互更加人性化而發(fā)明出來的。因為JavaScript是一門解釋型的語言,而解釋器內(nèi)嵌于瀏覽器,這個解釋器是單線程的。
之所以不設(shè)計成多線程是因為渲染網(wǎng)頁的時候多線程容易引起死鎖或者資源沖突等問題。但是瀏覽器本身是多線程的,比如解釋運行JavaScript的同時還在加載網(wǎng)絡(luò)資源。

Why doesn"t JavaScript support multithreading?

事件循環(huán)

單線程就意味著如果你要運行很多命令,那么這些命令需要排序,一般情況下,這些命令是從上到下排序執(zhí)行(因為解釋器是從文件頂部開始)。比如以下代碼是按照順序執(zhí)行的。

console.log("1");
console.log("2");
console.log("3");
//1
//2
//3

但是我們還有知道在JavaScript里有異步編程的說法,比如Ajax,setTimeout,setInterval或者ES6中的Promise,async,await。

那么什么是同步和異步呢?

一條命令的執(zhí)行在計算機(jī)里的意思就是它此時在使用CPU等資源,那么因為想要獲得CPU資源的命令有很多,而CPU執(zhí)行命令也需要時間去運算獲得結(jié)果,于是就有了同步異步的概念。

同步就是在發(fā)出一個CPU請求時,在沒有得到結(jié)果之前,該CPU請求就不返回。但是一旦調(diào)用返回,就得到返回值了。

異步表示CPU請求在發(fā)出之后,這個調(diào)用就直接返回了,所以沒有返回結(jié)果。在運行結(jié)束后,需要通過一系列手段來獲得返回值

這時候就要引入進(jìn)程和線程的概念。

進(jìn)程與線程 進(jìn)程

概念:進(jìn)程是一個具有一定獨立功能的程序在一個數(shù)據(jù)集上的一次動態(tài)執(zhí)行的過程,是操作系統(tǒng)進(jìn)行資源分配和調(diào)度的一個獨立單位,是應(yīng)用程序運行的載體。

線程

由于進(jìn)程對于CPU的使用是輪流的,那么就存在進(jìn)程的切換,但是由于現(xiàn)在的程序都比較大,切換的開銷很大會浪費CPU的資源,于是就發(fā)明了線程,把一個大的進(jìn)程分解成多個線程共同執(zhí)行。

區(qū)別

進(jìn)程是操作系統(tǒng)分配資源的最小單位,線程是程序執(zhí)行的最小單位。

一個進(jìn)程由一個或多個線程組成,線程是一個進(jìn)程中代碼的不同執(zhí)行路線;

進(jìn)程之間相互獨立,但同一進(jìn)程下的各個線程之間共享程序的內(nèi)存空間(包括代碼段、數(shù)據(jù)集、堆等)及一些進(jìn)程級的資源(如打開文件和信號)。

調(diào)度和切換:線程上下文切換比進(jìn)程上下文切換要快得多。

舉個例子

假如我是鳴人,我想吃很多拉面,如果我一個人吃10碗的話,那我就是一個進(jìn)程一個線程完成吃拉面這件事情。
但是如果我用9個分身和我一起吃10碗拉面,那我就是一個進(jìn)程用9個線程去完成吃拉面這件事情。
而多進(jìn)程這表示名人在一樂拉面里面吃拉面的同時,好色仙人在偷看妹子洗澡~ ~。好色仙人是單進(jìn)程單線程去偷看的哦!

瀏覽器的線程

瀏覽器的內(nèi)核是多線程的,在內(nèi)核控制下各線程相互配合以保持同步,一個瀏覽器通常由一下線程組成:

GUI 渲染線程

JavaScript引擎線程

事件觸發(fā)線程

異步http請求線程

EventLoop輪詢的處理線程

這些線程的作用:

UI線程用于渲染頁面

js線程用于執(zhí)行js任務(wù)

瀏覽器事件觸發(fā)線程用于控制交互,響應(yīng)用戶

http線程用于處理請求,ajax是委托給瀏覽器新開一個http線程

EventLoop處理線程用于輪詢消息隊列

JavaScript事件循環(huán)和消息隊列(瀏覽器環(huán)境)

因為JavaScript是單線程的,而瀏覽器是多線程的,所以為了執(zhí)行不同的同步異步的代碼,JavaScript運行的環(huán)境采用里事件循環(huán)和消息隊列來達(dá)到目的。
每個線程的任務(wù)執(zhí)行順序都是FIFO(先進(jìn)先出)
在JavaScript運行的環(huán)境中,有一個負(fù)責(zé)程序本身的運行,作為主線程;另一個負(fù)責(zé)主線程與其他線程的通信,被稱為Event Loop 線程。
每當(dāng)主線程遇到異步的任務(wù),把他們移入到Event Loop 線程,然后主線程繼續(xù)運行,等到主線程完全運行完之后,再去Event Loop 線程拿結(jié)果。
而每個異步任務(wù)都包含著與它相關(guān)聯(lián)的信息,比如運行狀態(tài),回調(diào)函數(shù)等。

由此我們可以知道,同步任務(wù)和異步任務(wù)會被分發(fā)到不同的線程去執(zhí)行。
現(xiàn)在我們就可以分析一下一下代碼的運行結(jié)果了。

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

因為setTimeout是異步的事件,所以主線程把它調(diào)入Event Loop線程進(jìn)行注冊。

主線程繼續(xù)執(zhí)行console.log("我是第一");

主線程執(zhí)行完畢,從Event Loop 線程讀取回調(diào)函數(shù)。再執(zhí)行console.log("我才是第一");;

setTimeout 和 setInterval setTimeout

這里值得一提的是,setTimeout(callback,0)指的是主線程中的同步任務(wù)運行完了之后立刻由Event Loop 線程調(diào)入主線程。
而計時是在調(diào)入Event Loop線程注冊時開始的,此時setTimeout的回調(diào)函數(shù)執(zhí)行時間與主線程運行結(jié)束的時間相關(guān)。
關(guān)于setTimeout要補充的是,即便主線程為空,0毫秒實際上也是達(dá)不到的。根據(jù)HTML的標(biāo)準(zhǔn),最低是4毫秒。

setInterval

需要注意的是,此函數(shù)是每隔一段時間將回調(diào)函數(shù)放入Event Loop線程。
一旦setInterval的回調(diào)函數(shù)fn執(zhí)行時間超過了延遲時間ms,那么就完全看不出來有時間間隔了

micro-task(微任務(wù))macro-task(宏任務(wù))

Event Loop線程中包含任務(wù)隊列(用來對不同優(yōu)先級的異步事件進(jìn)行排序),而任務(wù)隊列又分為macro-task(宏任務(wù))micro-task(微任務(wù)),在最新標(biāo)準(zhǔn)中,它們被分別稱為taskjobs。

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

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

setTimeout/Promise等我們稱之為任務(wù)源。而進(jìn)入任務(wù)隊列的是他們指定的具體執(zhí)行任務(wù)(回調(diào)函數(shù))。

來自不同的任務(wù)源的任務(wù)會進(jìn)入到不同的任務(wù)隊列中,而不同的任務(wù)隊列執(zhí)行過程如下:
執(zhí)行過程如下:
JavaScript引擎首先從macro-task中取出第一個任務(wù),
執(zhí)行完畢后,將micro-task中的所有任務(wù)取出,按順序全部執(zhí)行;
然后再從macro-task中取下一個,
執(zhí)行完畢后,再次將micro-task中的全部取出;
循環(huán)往復(fù),直到兩個隊列中的任務(wù)都取完。

舉個大例子
console.log("start");
var promise = new Promise((resolve) => {
    console.log("promise start..");
    resolve("promise");
}); //3
promise.then((val) => console.log(val));
setTimeout(()=>{console.log("setTime1")},0);
console.log("test end...")

這里我們按順序來分析。

第一輪

整體script代碼作為一個宏任務(wù)進(jìn)入主線程,運行console.log("start");。

然后遇到Promises直接運行console.log("promise start..")。

然后遇到promise.then,存入到micro-task隊列中。

然后遇到setTimeout,存入到macro-task隊列中。

于然后運行console.log("test end...");

在這一輪中,宏任務(wù)運行結(jié)束,運行micro-task隊列中的 promise.then,輸出promise

第二輪

取出macro-task隊列中的setTimeout,運行console.log("setTime1");

結(jié)果

輸出的順序就是

// start
// promise start
// test end...
// promise
//setTime1
留一個案例你們?nèi)シ治?/b>
async function testSometing() {
    console.log("執(zhí)行testSometing");
    return "testSometing";
}

async function testAsync() {
    console.log("執(zhí)行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...")
感謝以下文章

前端基礎(chǔ)進(jìn)階(十二):深入核心,詳解事件循環(huán)機(jī)制

[[JavaScript] Macrotask Queue和Microtask Queue](http://www.jianshu.com/p/3ed9...

JavaScript運行機(jī)制(堆、棧、消息隊列)

JavaScript 運行機(jī)制詳解:再談Event Loop

這一次,徹底弄懂 JavaScript 執(zhí)行機(jī)制

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/90077.html

相關(guān)文章

  • JS學(xué)習(xí)筆記 - 分析 JavaScript執(zhí)行順序

    摘要:瀏覽器在解析文檔流的時候,如果遇到一個標(biāo)簽,則會等到這個代碼塊都加載完之后再對代碼進(jìn)行預(yù)編譯,然后在執(zhí)行。執(zhí)行完畢后,瀏覽器會繼續(xù)解析西門的文檔流,同時也準(zhǔn)備好處理下一個代碼塊。同時,也避開了文檔流對執(zhí)行的限制。 本文章記錄本人在學(xué)習(xí) JavaScript 中看書理解到的一些東西,加深記憶和并且整理記錄下來,方便之后的復(fù)習(xí)。 在 html 文檔中的執(zhí)行順序 js代碼執(zhí)行順序...

    Keagan 評論0 收藏0
  • 從連續(xù)賦值到:詞法分析、函數(shù)執(zhí)行原理

    摘要:先說下這個老話題連續(xù)賦值例結(jié)果是什么這句簡單,而這句呢答案是,變成了全局變量了這是實際執(zhí)行順序未使用聲明,所以變?nèi)肿兞苛死茉缫郧暗拿嬖囶}目了,相信很多人知道答案,考點詞法分析執(zhí)行順序運算符優(yōu)先級等這是我理解的實際執(zhí)行順序我是這么猜想的自 先說下這個老話題:連續(xù)賦值 例1: function a(){ var o1 = o2 = 5; } a(); console.l...

    rose 評論0 收藏0
  • JavaScript的預(yù)編譯過程分析

    摘要:一概念是一個單線程解釋型的編程語言。預(yù)編譯大致可分為步創(chuàng)建對象找形參和變量聲明,將形參和變量名作為屬性名,值為將實參值和形參統(tǒng)一在函數(shù)體里面找函數(shù)聲明,值賦予函數(shù)體。 一、JavaScript概念 JavaScript ( JS ) 是一個單線程、解釋型的編程語言。 二、JavaScript語言特點 2.1 單線程 JavaScript語言的一大特點就是單線程,也就是說,同一個時間只能...

    graf 評論0 收藏0
  • javascript引擎執(zhí)行的過程的理解--執(zhí)行階段

    摘要:如果對語法分析和預(yù)編譯,還有疑問引擎執(zhí)行的過程的理解語法分析和預(yù)編譯階段。參與執(zhí)行過程的線程分別是引擎線程也稱為內(nèi)核,負(fù)責(zé)解析執(zhí)行腳本程序的主線程例如引擎。以上便是引擎執(zhí)行宏任務(wù)的整個過程。 一、概述 js引擎執(zhí)行過程主要分為三個階段,分別是語法分析,預(yù)編譯和執(zhí)行階段,上篇文章我們介紹了語法分析和預(yù)編譯階段,那么我們先做個簡單概括,如下: 1、語法分析: 分別對加載完成的代碼塊進(jìn)行語法...

    SnaiLiu 評論0 收藏0
  • javascript引擎執(zhí)行的過程的理解--執(zhí)行階段

    摘要:如果對語法分析和預(yù)編譯,還有疑問引擎執(zhí)行的過程的理解語法分析和預(yù)編譯階段。參與執(zhí)行過程的線程分別是引擎線程也稱為內(nèi)核,負(fù)責(zé)解析執(zhí)行腳本程序的主線程例如引擎。以上便是引擎執(zhí)行宏任務(wù)的整個過程。一、概述 js引擎執(zhí)行過程主要分為三個階段,分別是語法分析,預(yù)編譯和執(zhí)行階段,上篇文章我們介紹了語法分析和預(yù)編譯階段,那么我們先做個簡單概括,如下: 1、語法分析: 分別對加載完成的代碼塊進(jìn)行語法檢驗,語...

    Achilles 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<