成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

JavaScript執(zhí)行順序分析

chnmagnus / 2981人閱讀

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

前言

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

單線程JavaScript

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

Why doesn"t JavaScript support multithreading?

事件循環(huán)

單線程就意味著如果你要運(yùn)行很多命令,那么這些命令需要排序,一般情況下,這些命令是從上到下排序執(zhí)行(因?yàn)榻忉屍魇菑奈募敳块_(kāi)始)。比如以下代碼是按照順序執(zhí)行的。

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

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

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

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

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

異步表示CPU請(qǐng)求在發(fā)出之后,這個(gè)調(diào)用就直接返回了,所以沒(méi)有返回結(jié)果。在運(yùn)行結(jié)束后,需要通過(guò)一系列手段來(lái)獲得返回值

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

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

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

線程

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

區(qū)別

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

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

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

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

舉個(gè)例子

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

瀏覽器的線程

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

GUI 渲染線程

JavaScript引擎線程

事件觸發(fā)線程

異步http請(qǐng)求線程

EventLoop輪詢的處理線程

這些線程的作用:

UI線程用于渲染頁(yè)面

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

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

http線程用于處理請(qǐng)求,ajax是委托給瀏覽器新開(kāi)一個(gè)http線程

EventLoop處理線程用于輪詢消息隊(duì)列

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

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

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

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

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

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

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

setTimeout 和 setInterval setTimeout

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

setInterval

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

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

Event Loop線程中包含任務(wù)隊(duì)列(用來(lái)對(duì)不同優(yōu)先級(jí)的異步事件進(jìn)行排序),而任務(wù)隊(duì)列又分為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ù)隊(duì)列的是他們指定的具體執(zhí)行任務(wù)(回調(diào)函數(shù))。

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

舉個(gè)大例子
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...")

這里我們按順序來(lái)分析。

第一輪

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

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

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

然后遇到setTimeout,存入到macro-task隊(duì)列中。

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

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

第二輪

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

結(jié)果

輸出的順序就是

// start
// promise start
// test end...
// promise
//setTime1
留一個(gè)案例你們?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運(yùn)行機(jī)制(堆、棧、消息隊(duì)列)

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

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

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/90077.html

相關(guān)文章

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

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

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

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

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

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

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

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

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

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

    Achilles 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<