摘要:瀏覽器中與中事件循環(huán)與執(zhí)行機制不同,不可混為一談。瀏覽器環(huán)境執(zhí)行為單線程不考慮,所有代碼皆在執(zhí)行線程調(diào)用棧完成執(zhí)行。參考文章強烈推薦不要混淆和瀏覽器中的強烈推薦中的模塊強烈推薦理解事件循環(huán)一淺析定時器詳解
注意
在 node 11 版本中,node 下 Event Loop 已經(jīng)與瀏覽器趨于相同。背景
在 node 11 版本中,node 下 Event Loop 已經(jīng)與瀏覽器趨于相同。
在 node 11 版本中,node 下 Event Loop 已經(jīng)與瀏覽器趨于相同。
Event Loop也是js老生常談的一個話題了。2月底看了阮一峰老師的《Node定時器詳解》一文后,發(fā)現(xiàn)無法完全對標之前看過的js事件循環(huán)執(zhí)行機制,又查閱了一些其他資料,記為筆記,感覺不妥,總結(jié)成文。
瀏覽器中與node中事件循環(huán)與執(zhí)行機制不同,不可混為一談。
瀏覽器的Event loop是在HTML5中定義的規(guī)范,而node中則由libuv庫實現(xiàn)。同時閱讀《深入淺出nodeJs》一書時發(fā)現(xiàn)比較當時node機制已有不同,所以本文node部分針對為此文發(fā)布時版本。強烈推薦讀下參考鏈接中的前三篇。
js執(zhí)行為單線程(不考慮web worker),所有代碼皆在執(zhí)行線程調(diào)用棧完成執(zhí)行。當執(zhí)行線程任務(wù)清空后才會去輪詢?nèi)∪蝿?wù)隊列中任務(wù)。
任務(wù)隊列異步任務(wù)分為task(宏任務(wù),也可稱為macroTask)和microtask(微任務(wù))兩類。
當滿足執(zhí)行條件時,task和microtask會被放入各自的隊列中等待放入執(zhí)行線程執(zhí)行,我們把這兩個隊列稱為Task Queue(也叫Macrotask Queue)和Microtask Queue。
task:script中代碼、setTimeout、setInterval、I/O、UI render。
microtask: promise、Object.observe、MutationObserver。
具體過程執(zhí)行完主執(zhí)行線程中的任務(wù)。
取出Microtask Queue中任務(wù)執(zhí)行直到清空。
取出Macrotask Queue中一個任務(wù)執(zhí)行。
取出Microtask Queue中任務(wù)執(zhí)行直到清空。
重復(fù)3和4。
即為同步完成,一個宏任務(wù),所有微任務(wù),一個宏任務(wù),所有微任務(wù)......
注意在瀏覽器頁面中可以認為初始執(zhí)行線程中沒有代碼,每一個script標簽中的代碼是一個獨立的task,即會執(zhí)行完前面的script中創(chuàng)建的microtask再執(zhí)行后面的script中的同步代碼。
如果microtask一直被添加,則會繼續(xù)執(zhí)行microtask,“卡死”macrotask。
部分版本瀏覽器有執(zhí)行順序與上述不符的情況,可能是不符合標準或js與html部分標準沖突。可閱讀參考文章中第一篇。
new Promise((resolve, reject) =>{console.log(‘同步’);resolve()}).then(() => {console.log("異步")}),即promise的then和catch才是microtask,本身的內(nèi)部代碼不是。
個別瀏覽器獨有API未列出。
偽代碼while (true) { 宏任務(wù)隊列.shift() 微任務(wù)隊列全部任務(wù)() }node環(huán)境
js執(zhí)行為單線程,所有代碼皆在執(zhí)行線程調(diào)用棧完成執(zhí)行。當執(zhí)行線程任務(wù)清空后才會去輪詢?nèi)∪蝿?wù)隊列中任務(wù)。
循環(huán)階段在node中事件每一輪循環(huán)按照順序分為6個階段,來自libuv的實現(xiàn):
timers:執(zhí)行滿足條件的setTimeout、setInterval回調(diào)。
I/O callbacks:是否有已完成的I/O操作的回調(diào)函數(shù),來自上一輪的poll殘留。
idle,prepare:可忽略
poll:等待還沒完成的I/O事件,會因timers和超時時間等結(jié)束等待。
check:執(zhí)行setImmediate的回調(diào)。
close callbacks:關(guān)閉所有的closing handles,一些onclose事件。
執(zhí)行機制 幾個隊列除上述循環(huán)階段中的任務(wù)類型,我們還剩下瀏覽器和node共有的microtask和node獨有的process.nextTick,我們稱之為Microtask Queue和NextTick Queue。
我們把循環(huán)中的幾個階段的執(zhí)行隊列也分別稱為Timers Queue、I/O Queue、Check Queue、Close Queue。
循環(huán)之前在進入第一次循環(huán)之前,會先進行如下操作:
同步任務(wù)
發(fā)出異步請求
規(guī)劃定時器生效的時間
執(zhí)行process.nextTick()
開始循環(huán)按照我們的循環(huán)的6個階段依次執(zhí)行,每次拿出當前階段中的全部任務(wù)執(zhí)行,清空NextTick Queue,清空Microtask Queue。再執(zhí)行下一階段,全部6個階段執(zhí)行完畢后,進入下輪循環(huán)。即:
清空當前循環(huán)內(nèi)的Timers Queue,清空NextTick Queue,清空Microtask Queue。
清空當前循環(huán)內(nèi)的I/O Queue,清空NextTick Queue,清空Microtask Queue。
清空當前循環(huán)內(nèi)的Check Queu,清空NextTick Queue,清空Microtask Queue。
清空當前循環(huán)內(nèi)的Close Queu,清空NextTick Queue,清空Microtask Queue。
進入下輪循環(huán)。
可以看出,nextTick優(yōu)先級比promise等microtask高。setTimeout和setInterval優(yōu)先級比setImmediate高。
注意如果在timers階段執(zhí)行時創(chuàng)建了setImmediate則會在此輪循環(huán)的check階段執(zhí)行,如果在timers階段創(chuàng)建了setTimeout,由于timers已取出完畢,則會進入下輪循環(huán),check階段創(chuàng)建timers任務(wù)同理。
setTimeout優(yōu)先級比setImmediate高,但是由于setTimeout(fn,0)的真正延遲不可能完全為0秒,可能出現(xiàn)先創(chuàng)建的setTimeout(fn,0)而比setImmediate的回調(diào)后執(zhí)行的情況。
偽代碼while (true) { loop.forEach((階段) => { 階段全部任務(wù)() nextTick全部任務(wù)() microTask全部任務(wù)() }) loop = loop.next }測試代碼
function sleep(time) { let startTime = new Date() while (new Date() - startTime < time) {} console.log("1s over") } setTimeout(() => { console.log("setTimeout - 1") setTimeout(() => { console.log("setTimeout - 1 - 1") sleep(1000) }) new Promise(resolve => resolve()).then(() => { console.log("setTimeout - 1 - then") new Promise(resolve => resolve()).then(() => { console.log("setTimeout - 1 - then - then") }) }) sleep(1000) }) setTimeout(() => { console.log("setTimeout - 2") setTimeout(() => { console.log("setTimeout - 2 - 1") sleep(1000) }) new Promise(resolve => resolve()).then(() => { console.log("setTimeout - 2 - then") new Promise(resolve => resolve()).then(() => { console.log("setTimeout - 2 - then - then") }) }) sleep(1000) })
瀏覽器輸出:
setTimeout - 1 //1為單個task 1s over setTimeout - 1 - then setTimeout - 1 - then - then setTimeout - 2 //2為單個task 1s over setTimeout - 2 - then setTimeout - 2 - then - then setTimeout - 1 - 1 1s over setTimeout - 2 - 1 1s over
node輸出:
setTimeout - 1 1s over setTimeout - 2 //1、2為單階段task 1s over setTimeout - 1 - then setTimeout - 2 - then setTimeout - 1 - then - then setTimeout - 2 - then - then setTimeout - 1 - 1 1s over setTimeout - 2 - 1 1s over
由此也可看出事件循環(huán)在瀏覽器和node中的不同。
參考文章Tasks, microtasks, queues and schedules 強烈推薦
不要混淆nodejs和瀏覽器中的event loop 強烈推薦
node中的Event模塊 強烈推薦
理解事件循環(huán)一(淺析)
Node 定時器詳解
???
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/93347.html
摘要:如果沒到毫秒,那么階段就會跳過,進入階段,先執(zhí)行的回調(diào)函數(shù)。參考文檔什么是瀏覽器的事件循環(huán)不要混淆和瀏覽器中的定時器詳解瀏覽器和不同的事件循環(huán)深入理解事件循環(huán)機制篇中的執(zhí)行機制 最近對Event loop比較感興趣,所以了解了一下。但是發(fā)現(xiàn)整個Event loop盡管有很多篇文章,但是沒有一篇可以看完就對它所有內(nèi)容都了解的文章。大部分的文章都只闡述了瀏覽器或者Node二者之一,沒有對比...
摘要:事件觸發(fā)線程主要負責將準備好的事件交給引擎線程執(zhí)行。它將不同的任務(wù)分配給不同的線程,形成一個事件循環(huán),以異步的方式將任務(wù)的執(zhí)行結(jié)果返回給引擎。 Fundebug經(jīng)作者浪里行舟授權(quán)首發(fā),未經(jīng)同意請勿轉(zhuǎn)載。 前言 本文我們將會介紹 JS 實現(xiàn)異步的原理,并且了解了在瀏覽器和 Node 中 Event Loop 其實是不相同的。 一、線程與進程 1. 概念 我們經(jīng)常說 JS 是單線程執(zhí)行的,...
摘要:新加了一個微任務(wù)和一個宏任務(wù)在當前執(zhí)行棧的尾部下一次之前觸發(fā)回調(diào)函數(shù)。階段這個階段主要執(zhí)行一些系統(tǒng)操作帶來的回調(diào)函數(shù),如錯誤,如果嘗試鏈接時出現(xiàn)錯誤,一些會把這個錯誤報告給。 JavaScript引擎又稱為JavaScript解釋器,是JavaScript解釋為機器碼的工具,分別運行在瀏覽器和Node中。而根據(jù)上下文的不同,Event loop也有不同的實現(xiàn):其中Node使用了libu...
摘要:心塞塞根據(jù)規(guī)范,事件循環(huán)是通過任務(wù)隊列的機制來進行協(xié)調(diào)的。等便是任務(wù)源,而進入任務(wù)隊列的是他們指定的具體執(zhí)行任務(wù)回調(diào)函數(shù)。然后當前本輪的結(jié)束,主線程可以繼續(xù)取下一個執(zhí)行。 依然是:經(jīng)濟基礎(chǔ)決定上層建筑。 說明 首先,旨在搞清常用的同步異步執(zhí)行機制 其次,暫時不討論node.js的Event Loop執(zhí)行機制,以下關(guān)于瀏覽器的Event Loop執(zhí)行機制 最后,借鑒了很多前輩的研究文...
摘要:的單線程,與它的用途有關(guān)。特點的顯著特點異步機制事件驅(qū)動。隊列的讀取輪詢線程,事件的消費者,的主角。它將不同的任務(wù)分配給不同的線程,形成一個事件循環(huán),以異步的方式將任務(wù)的執(zhí)行結(jié)果返回給引擎。 這兩天跟同事同事討論遇到的一個問題,js中的event loop,引出了chrome與node中運行具有setTimeout和Promise的程序時候執(zhí)行結(jié)果不一樣的問題,從而引出了Nodejs的...
閱讀 1381·2019-08-30 15:44
閱讀 2148·2019-08-30 13:49
閱讀 1751·2019-08-26 13:54
閱讀 3570·2019-08-26 10:20
閱讀 3431·2019-08-23 17:18
閱讀 3363·2019-08-23 17:05
閱讀 2201·2019-08-23 15:38
閱讀 1087·2019-08-23 14:35