先來明白些概念性內(nèi)容。
進(jìn)程、線程
進(jìn)程是系統(tǒng)分配的獨(dú)立資源,是 CPU 資源分配的基本單位,進(jìn)程是由一個(gè)或者多個(gè)線程組成的。
線程是進(jìn)程的執(zhí)行流,是CPU調(diào)度和分派的基本單位,同個(gè)進(jìn)程之中的多個(gè)線程之間是共享該進(jìn)程的資源的。
瀏覽器內(nèi)核
瀏覽器是多進(jìn)程的,瀏覽器每一個(gè) tab 標(biāo)簽都代表一個(gè)獨(dú)立的進(jìn)程(也不一定,因?yàn)槎鄠€(gè)空白 tab 標(biāo)簽會(huì)合并成一個(gè)進(jìn)程),瀏覽器內(nèi)核(瀏覽器渲染進(jìn)程)屬于瀏覽器多進(jìn)程中的一種。
瀏覽器內(nèi)核有多種線程在工作。
GUI 渲染線程:
JS 引擎線程:
事件觸發(fā)線程:
當(dāng)事件符合觸發(fā)條件被觸發(fā)時(shí),該線程會(huì)把對應(yīng)的事件回調(diào)函數(shù)添加到任務(wù)隊(duì)列的隊(duì)尾,等待 JS 引擎處理。
定時(shí)器觸發(fā)線程:
http 請求線程:

JavaScript 引擎是單線程
JavaScript 引擎是單線程,也就是說每次只能執(zhí)行一項(xiàng)任務(wù),其他任務(wù)都得按照順序排隊(duì)等待被執(zhí)行,只有當(dāng)前的任務(wù)執(zhí)行完成之后才會(huì)往下執(zhí)行下一個(gè)任務(wù)。
HTML5 中提出了 Web-Worker API,主要是為了解決頁面阻塞問題,但是并沒有改變 JavaScript 是單線程的本質(zhì)。了解 Web-Worker。
JavaScript 事件循環(huán)機(jī)制
JavaScript 事件循環(huán)機(jī)制分為瀏覽器和 Node 事件循環(huán)機(jī)制,兩者的實(shí)現(xiàn)技術(shù)不一樣,瀏覽器 Event Loop 是 HTML 中定義的規(guī)范,Node Event Loop 是由 libuv 庫實(shí)現(xiàn)。這里主要講的是瀏覽器部分。
Javascript 有一個(gè) main thread 主線程和 call-stack 調(diào)用棧(執(zhí)行棧),所有的任務(wù)都會(huì)被放到調(diào)用棧等待主線程執(zhí)行。
JS 調(diào)用棧
JS 調(diào)用棧是一種后進(jìn)先出的數(shù)據(jù)結(jié)構(gòu)。當(dāng)函數(shù)被調(diào)用時(shí),會(huì)被添加到棧中的頂部,執(zhí)行完成之后就從棧頂部移出該函數(shù),直到棧內(nèi)被清空。
同步任務(wù)、異步任務(wù)
JavaScript 單線程中的任務(wù)分為同步任務(wù)和異步任務(wù)。同步任務(wù)會(huì)在調(diào)用棧中按照順序排隊(duì)等待主線程執(zhí)行,異步任務(wù)則會(huì)在異步有了結(jié)果后將注冊的回調(diào)函數(shù)添加到任務(wù)隊(duì)列(消息隊(duì)列)中等待主線程空閑的時(shí)候,也就是棧內(nèi)被清空的時(shí)候,被讀取到棧中等待主線程執(zhí)行。任務(wù)隊(duì)列是先進(jìn)先出的數(shù)據(jù)結(jié)構(gòu)。
Event Loop
調(diào)用棧中的同步任務(wù)都執(zhí)行完畢,棧內(nèi)被清空了,就代表主線程空閑了,這個(gè)時(shí)候就會(huì)去任務(wù)隊(duì)列中按照順序讀取一個(gè)任務(wù)放入到棧中執(zhí)行。每次棧內(nèi)被清空,都會(huì)去讀取任務(wù)隊(duì)列有沒有任務(wù),有就讀取執(zhí)行,一直循環(huán)讀取-執(zhí)行的操作,就形成了事件循環(huán)。

定時(shí)器
定時(shí)器會(huì)開啟一條定時(shí)器觸發(fā)線程來觸發(fā)計(jì)時(shí),定時(shí)器會(huì)在等待了指定的時(shí)間后將事件放入到任務(wù)隊(duì)列中等待讀取到主線程執(zhí)行。
定時(shí)器指定的延時(shí)毫秒數(shù)其實(shí)并不準(zhǔn)確,因?yàn)槎〞r(shí)器只是在到了指定的時(shí)間時(shí)將事件放入到任務(wù)隊(duì)列中,必須要等到同步的任務(wù)和現(xiàn)有的任務(wù)隊(duì)列中的事件全部執(zhí)行完成之后,才會(huì)去讀取定時(shí)器的事件到主線程執(zhí)行,中間可能會(huì)存在耗時(shí)比較久的任務(wù),那么就不可能保證在指定的時(shí)間執(zhí)行。
宏任務(wù)(macro-task)、微任務(wù)(micro-task)
除了廣義的同步任務(wù)和異步任務(wù),JavaScript 單線程中的任務(wù)可以細(xì)分為宏任務(wù)和微任務(wù)。
macro-task包括:script(整體代碼), setTimeout, setInterval, setImmediate, I/O, UI rendering。
micro-task包括:process.nextTick, Promises, Object.observe, MutationObserver。
console.log(1);setTimeout(function() { console.log(2);})var promise = new Promise(function(resolve, reject) { console.log(3); resolve();})promise.then(function() { console.log(4);})console.log(5);示例中,setTimeout 和 Promise被稱為任務(wù)源,來自不同的任務(wù)源注冊的回調(diào)函數(shù)會(huì)被放入到不同的任務(wù)隊(duì)列中。
有了宏任務(wù)和微任務(wù)的概念后,那 JS 的執(zhí)行順序是怎樣的?是宏任務(wù)先還是微任務(wù)先?
第一次事件循環(huán)中,JavaScript 引擎會(huì)把整個(gè) script 代碼當(dāng)成一個(gè)宏任務(wù)執(zhí)行,執(zhí)行完成之后,再檢測本次循環(huán)中是否尋在微任務(wù),存在的話就依次從微任務(wù)的任務(wù)隊(duì)列中讀取執(zhí)行完所有的微任務(wù),再讀取宏任務(wù)的任務(wù)隊(duì)列中的任務(wù)執(zhí)行,再執(zhí)行所有的微任務(wù),如此循環(huán)。JS 的執(zhí)行順序就是每次事件循環(huán)中的宏任務(wù)-微任務(wù)。
以上所述是小編給大家介紹的JS瀏覽器事件循環(huán)機(jī)制詳解整合,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對VeVb武林網(wǎng)網(wǎng)站的支持!
新聞熱點(diǎn)
疑難解答