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

資訊專欄INFORMATION COLUMN

事件循環(huán)機制

CloudwiseAPM / 3040人閱讀

摘要:事件觸發(fā)線程主要負責(zé)將準(zhǔn)備好的事件交給引擎線程執(zhí)行。進程瀏覽器渲染進程瀏覽器內(nèi)核,主要負責(zé)頁面的渲染執(zhí)行以及事件的循環(huán)。第二輪循環(huán)結(jié)束。

將自己讀到的比較好的文章分享出來,大家互相學(xué)習(xí),各位大佬有好的文章也可以留個鏈接互相學(xué)習(xí),萬分感謝!
線程與進程

關(guān)于線程與進程的關(guān)系可以用下面的圖進行說明:

進程好比圖中的工廠,有多帶帶的專屬自己的工廠資源。

線程好比圖中的工人,多個工人在一個工廠中協(xié)作工作,工廠與工人是 1:n的關(guān)系。

多個工廠之間獨立存在。

而官方的說法是:

進程是 CPU資源分配的最小單位。

線程是 CPU調(diào)度的最小單位。

從更直觀的例子來看,可以打開任務(wù)管理器查看,第一個 tab便是進程列表,每一個進程占有的 CPU資源和內(nèi)存資源的比例很直觀的展示出來。

為什么js是單線程

初學(xué)計算機語言的時候,無論是 C、C++還是 JAVA,都是支持多線程,偏偏 JavaScript是單線程,不支持多線程,這也跟 JavaScript的作用有關(guān),都知道 JavaScript是主要運行在瀏覽器的腳本語言,最終操作的是頁面的 DOM結(jié)構(gòu),當(dāng)兩個 JavaScript腳本同時修改頁面的同一個 DOM節(jié)點時,瀏覽器該執(zhí)行哪個呢?所以當(dāng)時設(shè)計 JavaScript時,便要求當(dāng)前修改操作完成后方可進行下一步修改操作。

瀏覽器是支持多進程

同樣我們打開瀏覽器的任務(wù)管理器,以下圖為例:

瀏覽器的每一個 tab頁都是一個進程,有對應(yīng)的內(nèi)存占用空間、 CPU使用量以及進程ID。 新打開一個 tab頁時,都會新建一個進程,所以就有一個 tab頁對應(yīng)一個進程的說法,但是這種說法又是錯誤的,因為瀏覽器有自己的優(yōu)化機制,當(dāng)我們打開多個空白的 tab頁時,瀏覽器會將這多個空白頁的進程合并為一個,從而減少了進程的數(shù)量個數(shù)。

瀏覽器內(nèi)核

瀏覽器內(nèi)核中有多個進程在同步工作,今天涉及到的瀏覽器的進程主要包括以下進程:

Browser 進程

主進程,主要負責(zé)頁面管理以及管理其他進程的創(chuàng)建和銷毀等,常駐的線程有:

GUI渲染線程

JS引擎線程

事件觸發(fā)線程

定時器觸發(fā)線程

HTTP請求線程

GUI渲染線程

主要負責(zé)頁面的渲染,解析HTML、CSS,構(gòu)建DOM樹,布局和繪制等。

當(dāng)界面需要重繪或者由于某種操作引發(fā)回流時,將執(zhí)行該線程。

該線程與JS引擎線程互斥,當(dāng)執(zhí)行JS引擎線程時,GUI渲染會被掛起,當(dāng)任務(wù)隊列空閑時,JS引擎才會去執(zhí)行GUI渲染。

JS引擎線程

該線程當(dāng)然是主要負責(zé)處理 JavaScript腳本,執(zhí)行代碼。

也是主要負責(zé)執(zhí)行準(zhǔn)備好待執(zhí)行的事件,即定時器計數(shù)結(jié)束,或者異步請求成功并正確返回時,將依次進入任務(wù)隊列,等待 JS引擎線程的執(zhí)行。

當(dāng)然,該線程與 GUI渲染線程互斥,當(dāng) JS引擎線程執(zhí)行 JavaScript腳本時間過長,將導(dǎo)致頁面渲染的阻塞。

事件觸發(fā)線程

主要負責(zé)將準(zhǔn)備好的事件交給 JS引擎線程執(zhí)行。

比如 setTimeout定時器計數(shù)結(jié)束, ajax等異步請求成功并觸發(fā)回調(diào)函數(shù),或者用戶觸發(fā)點擊事件時,該線程會將整裝待發(fā)的事件依次加入到任務(wù)隊列的隊尾,等待 JS引擎線程的執(zhí)行。

定時器觸發(fā)線程

顧名思義,負責(zé)執(zhí)行異步定時器一類的函數(shù)的線程,如: setTimeout,setInterval。

主線程依次執(zhí)行代碼時,遇到定時器,會將定時器交給該線程處理,當(dāng)計數(shù)完畢后,事件觸發(fā)線程會將計數(shù)完畢后的事件加入到任務(wù)隊列的尾部,等待JS引擎線程執(zhí)行。

HTTP請求線程

顧名思義,負責(zé)執(zhí)行異步請求一類的函數(shù)的線程,如: Promise,anxios,ajax等。

主線程依次執(zhí)行代碼時,遇到異步請求,會將函數(shù)交給該線程處理,當(dāng)監(jiān)聽到狀態(tài)碼變更,如果有回調(diào)函數(shù),事件觸發(fā)線程會將回調(diào)函數(shù)加入到任務(wù)隊列的尾部,等待JS引擎線程執(zhí)行。

多個線程之間配合工作,各司其職。

Render 進程

瀏覽器渲染進程(瀏覽器內(nèi)核),主要負責(zé)頁面的渲染、JS執(zhí)行以及事件的循環(huán)。
同步任務(wù)和異步任務(wù)

同步任務(wù) 即可以立即執(zhí)行的任務(wù),例如 console.log() 打印一條日志、聲明一個變量或者執(zhí)行一次加法操作等。

異步任務(wù) 相反不會立即執(zhí)行的事件任務(wù)。異步任務(wù)包括宏任務(wù)微任務(wù)(后面會進行解釋~)。

常見的異步操作:

Ajax

DOM的事件操作

setTimeout

Promise的then方法

Node的讀取文件

下圖給出了同步任務(wù)與異步任務(wù)的執(zhí)行流程:

就像是一個容器,任務(wù)都是在棧中執(zhí)行。

主線程 就像是操作員,負責(zé)執(zhí)行棧中的任務(wù)。

任務(wù)隊列 就像是等待被加工的物品。

異步任務(wù)完成注冊后會將回調(diào)函數(shù)加入任務(wù)隊列等待主線程執(zhí)行。

執(zhí)行棧中的同步任務(wù)執(zhí)行完畢后,會查看并讀取任務(wù)隊列中的事件函數(shù),于是任務(wù)隊列的函數(shù)結(jié)束等待狀態(tài),進入執(zhí)行棧,開始執(zhí)行。

那么任務(wù)到底是如何入棧和出棧的呢?可以用一小段代碼進行解釋。

入棧與出棧

以下面的代碼為例:

    console.log(1);
    function fn1(){
        console.log(2);
    }
    function fn2(){
        console.log(3);
        fn1();
    }
    setTimeout(function(){
        console.log(4);
    }, 2000);
    fn2();
    console.log(5);

所以上面代碼運行的結(jié)果為:1,3,2,5,4。

宏任務(wù)和微任務(wù)

異步任務(wù)分為宏任務(wù)和微任務(wù),宏任務(wù)隊列可以有多個,微任務(wù)隊列只有一個。

宏任務(wù)和微任務(wù)的執(zhí)行方式在瀏覽器和 Node 中有差異。

宏任務(wù)(macrotask)
script(全局任務(wù)), setTimeout, setInterval, setImmediate, I/O, UI rendering
微任務(wù)(macrotask)
process.nextTick, Promise.then()Object.observe, MutationObserver

在微任務(wù)中 process.nextTick 優(yōu)先級高于Promise

當(dāng)一個異步任務(wù)入棧時,主線程判斷該任務(wù)為異步任務(wù),并把該任務(wù)交給異步處理模塊處理,當(dāng)異步處理模塊處理完打到觸發(fā)條件時,根據(jù)任務(wù)的類型,將回調(diào)函數(shù)壓入任務(wù)隊列。

如果是宏任務(wù),則新增一個宏任務(wù)隊列,任務(wù)隊列中的宏任務(wù)可以有多個來源。

如果是微任務(wù),則直接壓入微任務(wù)隊列。

所以上圖的任務(wù)隊列可以繼續(xù)細化一下:

那么當(dāng)棧為空時,宏任務(wù)和微任務(wù)的執(zhí)行機制又是什么呢?

Event Loop

到這里,除了上面的問題,我們已經(jīng)把事件循環(huán)的最基本的處理方式搞清楚了,但具體到異步任務(wù)中的宏任務(wù)和微任務(wù),還沒有弄明白。我們可以先順一遍執(zhí)行機制:

從全局任務(wù) script開始,任務(wù)依次進入棧中,被主線程執(zhí)行,執(zhí)行完后出棧。

遇到異步任務(wù),交給異步處理模塊處理,對應(yīng)的異步處理線程處理異步任務(wù)需要的操作,例如定時器的計數(shù)和異步請求監(jiān)聽狀態(tài)的變更。

當(dāng)異步任務(wù)達到可執(zhí)行狀態(tài)時,事件觸發(fā)線程將回調(diào)函數(shù)加入任務(wù)隊列,等待棧為空時,依次進入棧中執(zhí)行。

到這問題就來了,當(dāng)異步任務(wù)進入棧執(zhí)行時,是宏任務(wù)還是微任務(wù)呢?

由于執(zhí)行代碼入口都是全局任務(wù) script,而全局任務(wù)屬于宏任務(wù),所以當(dāng)棧為空,同步任務(wù)任務(wù)執(zhí)行完畢時,會先執(zhí)行微任務(wù)隊列里的任務(wù)。

微任務(wù)隊列里的任務(wù)全部執(zhí)行完畢后,會讀取宏任務(wù)隊列中拍最前的任務(wù)。

執(zhí)行宏任務(wù)的過程中,遇到微任務(wù),依次加入微任務(wù)隊列。

??蘸?,再次讀取微任務(wù)隊列里的任務(wù),依次類推。

實例解析

回到最開始的那段代碼,現(xiàn)在我們可以一步一步的看一下執(zhí)行順序。

console.log(1);
setTimeout(function(){
    console.log(2);
}, 0);
setTimeout(function(){
    console.log(3)
},2000)
console.log(4);

從全局任務(wù)入口,首先打印日志 1

遇到宏任務(wù) setTimeout,交給異步處理模塊,我們暫且先記為 setTimeout1,

再次遇到宏任務(wù) setTimeout,交給異步處理模塊,我們暫且先記為 setTimeout2,

順序執(zhí)行,打印日志 4,

此時同步任務(wù)已執(zhí)行完畢,讀取宏任務(wù)隊列的任務(wù),先執(zhí)行 setTimeout1的回調(diào)函數(shù),因為定時器的等待時間為 0秒,所以會直接輸出 2,但是 W3CHTML標(biāo)準(zhǔn)中規(guī)定,規(guī)定要求 setTimeout中低于 4ms的時間間隔算為 4ms

由于瀏覽器在執(zhí)行以上三步時,并未耗時很久,所以當(dāng)宏任務(wù) setTimeout1執(zhí)行完時, setTimeout2的等待時間并未結(jié)束,所以在 2秒后打印日志 3,實際上并未等待2秒。

下面我們可以再看一個實例:

    setTimeout(function(){
        console.log(1);
        Promise.resolve().then(function(){
            console.log(2)
        })
    },0)

    setTimeout(function(){
        console.log(3)
    },0)
    Promise.resolve().then(function(){
        console.log(4)
    });
    console.log(5)

當(dāng)代碼中遇到了異步請求的事件,又該如何執(zhí)行,根據(jù)上面總結(jié)的執(zhí)行機制,又該得到什么樣的結(jié)果?

第一輪循環(huán)

同樣從全局任務(wù)入口,遇到宏任務(wù) setTimeout,交給異步處理模塊,我們暫且先記為 setTimeout1,由于等待時間為 0,直接加入宏任務(wù)隊列。

再次遇到宏任務(wù) setTimeout,交給異步處理模塊,我們暫且先記為 setTimeout2,同樣直接加入宏任務(wù)隊列。

遇到微任務(wù) then(),加入微任務(wù)隊列。

最后遇到打印語句,直接打印日志 5。

第一輪循環(huán)結(jié)束后,可以畫出下圖:

第二輪循環(huán)

棧空后,先執(zhí)行微任務(wù)隊列中的 then()方法,輸出 4,此時微任務(wù)隊列為空。

讀取宏任務(wù)隊列的最靠前的任務(wù) setTimeout1。

先直接執(zhí)行打印語句,打印日志 1,又遇到微任務(wù) then(),加入微任務(wù)隊列。第二輪循環(huán)結(jié)束。

第三輪循環(huán)

先執(zhí)行微任務(wù)隊列中的 then()方法,輸出 2,此時微任務(wù)隊列為空。

繼續(xù)讀取宏任務(wù)隊列的最靠前的任務(wù) setTimeout2。

直接執(zhí)行打印語句,打印日志 3。第三輪循環(huán)結(jié)束,執(zhí)行完畢。

最后我們是我們的boss,歡迎大家在評論區(qū)留言寫出自己心中的那個正確答案。

console.log(1);

setTimeout(function(){
    console.log(2);
    new Promise(function(resolve, reject){
        console.log(3);
        resolve();
    }).then(function(){
        console.log(4);
    })
})

new Promise(function(resolve, reject){
    console.log(5);
    resolve();
}).then(function(){
    console.log(6);
})

setTimeout(function(){
    console.log(7)
})

setTimeout(function(){
    console.log(8);
    new Promise(function(resolve, reject){
        console.log(9);
        resolve();
    }).then(function(){
        console.log(10);
    })
})

new Promise(function(resolve){
    console.log(11);
    resolve();
}).then(function(){
    console.log(12)
})

console.log(13)

github地址:https://github.com/ABCDdouyaer/a_article_per_day/tree/master/0001
原文鏈接:https://mp.weixin.qq.com/s/9_hZX_xWSr3Gd1X_2_WOsA

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

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

相關(guān)文章

  • JavaScript運行機制事件循環(huán)

    摘要:主線程不斷重復(fù)上面的三步,此過程也就是常說的事件循環(huán)。所以主線程代碼執(zhí)行時間過長,會阻塞事件循環(huán)的執(zhí)行。參考資料這一次,徹底弄懂執(zhí)行機制任務(wù)隊列的順序機制事件循環(huán)搞懂異步事件輪詢與中的事件循環(huán) 1. 說明 讀過本文章后,您能知道: JavaScript代碼在瀏覽器中的執(zhí)行機制和事件循環(huán) 面試中經(jīng)常遇到的代碼輸出順序問題 首先通過一段代碼來驗證你是否了解代碼輸出順序,如果你不知道輸出...

    Ververica 評論0 收藏0
  • 事件循環(huán)機制(event loop)

    摘要:了解事件循環(huán)機制有助于理解的執(zhí)行過程,同時這也是面試常見題。那么這個回調(diào)函數(shù)將在何時由誰執(zhí)行呢已知是瀏覽器環(huán)境提供的,因此瀏覽器將對它進行處理,瀏覽器會在本次事件完成,即計時結(jié)束后,將回調(diào)函數(shù)加入循環(huán)隊列中,然后等待被加入執(zhí)行棧執(zhí)行。 如果有人問JavaScript是什么,也許你會說它是一個單線程、非阻塞、異步、解釋型的腳本語言。那么作為一個單線程語言,它是怎么實現(xiàn)非阻塞、異步的?這就...

    tain335 評論0 收藏0
  • 淺談不同環(huán)境下的JavaScript執(zhí)行機制 + 示例詳解

    摘要:如果沒有其他異步任務(wù)要處理比如到期的定時器,會一直停留在這個階段,等待請求返回結(jié)果。執(zhí)行的執(zhí)行事件關(guān)閉請求的,例如事件循環(huán)的每一次循環(huán)都需要依次經(jīng)過上述的階段。因此,才會早于執(zhí)行。 showImg(https://segmentfault.com/img/bVbnY76); 概念 同步任務(wù)(Synchronous) 在主線程上排隊執(zhí)行的任務(wù),只有前一個任務(wù)執(zhí)行完畢,才能執(zhí)行后一個任務(wù) ...

    wanghui 評論0 收藏0
  • Js 的事件循環(huán)(Event Loop)機制以及實例講解

    摘要:主線程要明確的一點是,主線程跟執(zhí)行棧是不同概念,主線程規(guī)定現(xiàn)在執(zhí)行執(zhí)行棧中的哪個事件。主線程循環(huán)即主線程會不停的從執(zhí)行棧中讀取事件,會執(zhí)行完所有棧中的同步代碼。以上參考資料詳解中的事件循環(huán)機制中的事件循環(huán)運行機制詳解再談 showImg(https://segmentfault.com/img/remote/1460000015317437?w=1920&h=1080); 前言 大家都...

    Anshiii 評論0 收藏0
  • 前端基礎(chǔ)進階(十二):深入核心,詳解事件循環(huán)機制

    摘要:前端基礎(chǔ)進階正是圍繞這條線索慢慢展開,而事件循環(huán)機制,則是這條線索的最關(guān)鍵的知識點。特別是中正式加入了對象之后,對于新標(biāo)準(zhǔn)中事件循環(huán)機制的理解就變得更加重要。之后全局上下文進入函數(shù)調(diào)用棧。 showImg(https://segmentfault.com/img/remote/1460000008811705); JavaScript的學(xué)習(xí)零散而龐雜,因此很多時候我們學(xué)到了一些東西,但...

    whjin 評論0 收藏0
  • JS JavaScript事件循環(huán)機制

    摘要:事件循環(huán)機制首先區(qū)分進程和線程進程是資源分配的最小單位系統(tǒng)會給它分配內(nèi)存不同的進程之間是可以同學(xué)的,如管道命名管道消息隊列一個進程里有單個或多個線程瀏覽器是多進程的,因為系統(tǒng)給它的進程分配了資源內(nèi)存打開會有一個主進程,每打開一個頁就有一個獨 JS JavaScript事件循環(huán)機制 首先區(qū)分進程和線程 進程是cpu資源分配的最小單位(系統(tǒng)會給它分配內(nèi)存) 不同的進程之間是可以同學(xué)的,如...

    dantezhao 評論0 收藏0

發(fā)表評論

0條評論

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