摘要:如果沒有其他異步任務(wù)要處理比如到期的定時(shí)器,會(huì)一直停留在這個(gè)階段,等待請(qǐng)求返回結(jié)果。執(zhí)行的執(zhí)行事件關(guān)閉請(qǐng)求的,例如事件循環(huán)的每一次循環(huán)都需要依次經(jīng)過上述的階段。因此,才會(huì)早于執(zhí)行。
概念 同步任務(wù)(Synchronous)
在主線程上排隊(duì)執(zhí)行的任務(wù),只有前一個(gè)任務(wù)執(zhí)行完畢,才能執(zhí)行后一個(gè)任務(wù)
異步任務(wù)(Asynchronous)不進(jìn)入主線程,而是進(jìn)入“任務(wù)隊(duì)列”的任務(wù),只有主線執(zhí)行棧清空,異步任務(wù)才進(jìn)入主線執(zhí)行棧執(zhí)行
任務(wù)隊(duì)列(Task Queue)包含有異步任務(wù)的隊(duì)列,包括“宏任務(wù)”與“微任務(wù)”
宏任務(wù)(Macrotasks / Task)創(chuàng)建文檔對(duì)象、解析 HTML、執(zhí)行主線程代碼(script)
執(zhí)行各種事件:頁面加載、輸入、點(diǎn)擊
setTimout,setInterval,setImmediate
I/O,Ajax,UI rendering
微任務(wù)(Microtasks / Jobs)process.nextTick
Promise.then
Object.observe(已廢棄)
MutationObserver
事件循環(huán)(Event Loop)事件循環(huán)是js實(shí)現(xiàn)異步的一種方法,也是js的執(zhí)行機(jī)制
JavaScript 主線程會(huì)在執(zhí)行棧清空后,讀取任務(wù)隊(duì)列,入棧第一個(gè)宏任務(wù),主線程執(zhí)行完該任務(wù)后又會(huì)先檢查微任務(wù)隊(duì)列并完成里面的所有微任務(wù),包括新創(chuàng)建的微任務(wù),完成一次事件循環(huán)。之后再次去讀取任務(wù)隊(duì)列,不斷循環(huán)
注意:
每次循環(huán)只會(huì)入棧一個(gè)宏任務(wù),所以多個(gè)宏任務(wù)需要多次事件循環(huán)才能執(zhí)行完
每次循環(huán)會(huì)執(zhí)行所有的微任務(wù),所以每次循環(huán)結(jié)束后微任務(wù)隊(duì)列被清空
timers:
執(zhí)行 setTimeout 和 setInterval 中到期的 callback
I/O callbacks:
除了以下操作的回調(diào)函數(shù),其他的回調(diào)函數(shù)都在這個(gè)階段執(zhí)行。
setTimeout,setInterval,setImmediate 的 callback
用于執(zhí)行 close 事件(關(guān)閉請(qǐng)求)的 callback,例如 socket.on("close", callback)
idle, prepare:
libuv 內(nèi)部調(diào)用
poll:
最為重要的階段,用于等待還未返回的 I/O 事件,比如服務(wù)器的回應(yīng)、用戶移動(dòng)鼠標(biāo)等等
這個(gè)階段的時(shí)間會(huì)比較長。如果沒有其他異步任務(wù)要處理(比如到期的定時(shí)器),會(huì)一直停留在這個(gè)階段,等待 I/O 請(qǐng)求返回結(jié)果。
check:
執(zhí)行 setImmediate 的 callback
close callbacks:
執(zhí)行 close 事件(關(guān)閉請(qǐng)求)的 callback,例如 socket.on("close", callback)
事件循環(huán)的每一次循環(huán)都需要依次經(jīng)過上述的階段。 每個(gè)階段都有自己的 callback 隊(duì)列,每當(dāng)進(jìn)入某個(gè)階段,都會(huì)從所屬的隊(duì)列中取出callback來執(zhí)行,當(dāng)隊(duì)列為空或者被執(zhí)行callback的數(shù)量達(dá)到系統(tǒng)的最大數(shù)量時(shí),進(jìn)入下一階段。這六個(gè)階段都執(zhí)行完畢稱為一輪循環(huán)
注意:
不同于瀏覽器的是,在每個(gè)階段完成后,microTask隊(duì)列就會(huì)被執(zhí)行,而不是MacroTask任務(wù)完成后。
每個(gè)階段完成后,微任務(wù)隊(duì)列就會(huì)被執(zhí)行。
如果在timers階段執(zhí)行時(shí)創(chuàng)建了setImmediate則會(huì)在此輪循環(huán)的check階段執(zhí)行,如果在timers階段創(chuàng)建了 setTimeout,由于timers已取出完畢,則會(huì)進(jìn)入下輪循環(huán),check階段創(chuàng)建timers任務(wù)同理
遞歸的調(diào)用 process.nextTick 會(huì)導(dǎo)致 I/O starving,官方推薦使用 setImmediate
瀏覽器環(huán)境下,microtask 的任務(wù)隊(duì)列是每個(gè) macrotask 執(zhí)行完之后執(zhí)行
Node.js中,microtask 會(huì)在事件循環(huán)的各個(gè)階段之間執(zhí)行,也就是一個(gè)階段執(zhí)行完畢,就會(huì)去執(zhí)行 microtask 隊(duì)列的任務(wù)
setTimeout(()=>{ console.log("timer1") Promise.resolve().then(function() { console.log("promise1") }) }, 0) setTimeout(()=>{ console.log("timer2") Promise.resolve().then(function() { console.log("promise2") }) }, 0) // 瀏覽器輸出: // time1 // promise1 // time2 // promise2 // Node輸出: // time1 // time2 // promise1 // promise2定時(shí)器 setTimeout(callback, time)
經(jīng)過指定時(shí)間后,把要執(zhí)行的任務(wù) callback 加入到任務(wù)隊(duì)列中
因?yàn)镴S是單線程,任務(wù)要一個(gè)一個(gè)執(zhí)行,如果前面的任務(wù)需要的時(shí)間太久,那么只能等著,導(dǎo)致真正的延遲時(shí)間可能遠(yuǎn)遠(yuǎn)大于指定時(shí)間(time ms)
setTimeout(callback, 0)指定某個(gè)任務(wù) callback 在主線程最早可得的空閑時(shí)間執(zhí)行,意思就是不用再等多少秒了,只要主線程執(zhí)行棧內(nèi)的同步任務(wù)全部執(zhí)行完成,棧為空就馬上執(zhí)行
0ms 實(shí)際上是不可能的,在瀏覽器中 setTimeout() / setInterval() 的每調(diào)用一次定時(shí)器的最小間隔 >=4ms,這通常是由于函數(shù)嵌套導(dǎo)致(嵌套層級(jí)達(dá)到一定深度),或者是由于已經(jīng)執(zhí)行的 setInterval 的回調(diào)函數(shù)阻塞導(dǎo)致的
在 Node.JS 環(huán)境為 1ms,但也取決于系統(tǒng)當(dāng)時(shí)的狀況
setTimeout(function () { console.log("1"); }, 0) console.log(2) // 輸出 2 1setInterval(callback, time)
每過指定時(shí)間(time ms),會(huì)有 callback 進(jìn)入任務(wù)隊(duì)列。
若 callback 執(zhí)行時(shí)間超過了指定時(shí)間,那么就會(huì)導(dǎo)致 callback 連續(xù)執(zhí)行,完全看不出來有時(shí)間間隔了
setImmediate(callback)Node.JS 特有定時(shí)器,在事件循環(huán)的 check 階段執(zhí)行
process.nextTick(callback)Node.JS 特有定時(shí)器,在事件循環(huán)各個(gè)階段結(jié)束后執(zhí)行
從技術(shù)上講,它不是事件循環(huán)的一部分
同循環(huán)下 process.nextTick 會(huì)優(yōu)于 Promise.then
Promise.resolve().then(() => console.log(1)); process.nextTick(() => console.log(2)); // 輸出 2 1注意
連續(xù)的 setTimeout,setImmediate 在再 timer 階段的執(zhí)行順序是不確定的,取決于系統(tǒng)當(dāng)時(shí)的狀況
但是把 setTimeout,setImmediate 放到一個(gè) I/O 回調(diào)里面,就一定是 setImmediate 先執(zhí)行,因?yàn)?poll 階段后面就是 check 階段
setImmediate(() => { console.log("timer1") Promise.resolve().then(function () { console.log("promise1") }) }) setTimeout(() => { console.log("timer2") Promise.resolve().then(function () { console.log("promise2") }) }, 0) // Node輸出: // timer1 timer2 // promise1 或者 promise2 // timer2 timer1 // promise2 promise1
fs.readFile("test.js", () => { setTimeout(() => console.log(1)); setImmediate(() => console.log(2)); }) // 輸出 2 1 // 先進(jìn)入 I/O callbacks 階段,然后是 check 階段,最后才是 下一次事件循環(huán)的 timers 階段。因此,setImmediate 才會(huì)早于setTimeout 執(zhí)行。示例
console.log(0) new Promise(function(resolve) { console.log(1); resolve(); }).then(function() { console.log(2) }) setTimeout(function() { console.log(3); new Promise(function(resolve) { console.log(4); resolve(); }).then(function() { console.log(5) }) }) new Promise(function(resolve) { console.log(6); resolve(); }).then(function() { console.log(7) }) setTimeout(function() { console.log(8); new Promise(function(resolve) { console.log(9); resolve(); }).then(function() { console.log(10) }) }) console.log(11)瀏覽器環(huán)境
第一輪事件循環(huán)宏任務(wù)
開始執(zhí)行代碼,遇到 console.log 輸出 0
接著遇到 new Promise 其回調(diào)函數(shù)作為同步任務(wù)直接執(zhí)行
遇到 console.log 輸出 1
遇到 then 回調(diào)函數(shù)作為異步任務(wù)進(jìn)入“微任務(wù)隊(duì)列”
接著遇到 setTimeout 其回調(diào)函數(shù)作為異步任務(wù)進(jìn)入“宏任務(wù)隊(duì)列”
接著遇到 new Promise 其回調(diào)函數(shù)作為同步任務(wù)直接執(zhí)行
遇到 console.log 輸出 6
遇到 then 回調(diào)函數(shù)作為異步任務(wù)進(jìn)入“微任務(wù)隊(duì)列”
接著遇到 setTimeout 其回調(diào)函數(shù)作為異步任務(wù)進(jìn)入“宏任務(wù)隊(duì)列”
最后遇到 console.log 輸出 11
此時(shí)第一輪事件循環(huán)宏任務(wù)結(jié)束,依次輸出 0 1 6 11
第一輪事件循環(huán)微任務(wù)
執(zhí)行注冊(cè)在“微任務(wù)隊(duì)列”里的微任務(wù),遇到 then 執(zhí)行其回調(diào)輸出 2
遇到 then 執(zhí)行其回調(diào)輸出 7
此時(shí)第一輪事件循環(huán)微任務(wù)結(jié)束,依次輸出 2 7
第一輪事件循環(huán)結(jié)束,此時(shí)“微任務(wù)隊(duì)列”已被清空,“宏任務(wù)隊(duì)列”里有兩個(gè) setTimeout 的回調(diào)函數(shù),第一個(gè) setTimeout 被送入主線程執(zhí)行棧
第二輪事件循環(huán)宏任務(wù)
開始執(zhí)行第一個(gè) setTimeout 回調(diào)函數(shù)
遇到 console.log 輸出 3
遇到 new Promise 其回調(diào)函數(shù)作為同步任務(wù)直接執(zhí)行
遇到 console.log 輸出 4
遇到 then 回調(diào)函數(shù)作為異步任務(wù)進(jìn)入“微任務(wù)隊(duì)列”
此時(shí)第二輪事件循環(huán)宏任務(wù)結(jié)束,依次輸出 3 4
第二輪事件循環(huán)微任務(wù)
執(zhí)行注冊(cè)在“微任務(wù)隊(duì)列”里的微任務(wù),遇到 then 執(zhí)行其回調(diào)輸出 5
此時(shí)第二輪事件循環(huán)微任務(wù)結(jié)束,輸出 5
第二輪事件循環(huán)結(jié)束,此時(shí)“微任務(wù)隊(duì)列”已被清空,“宏任務(wù)隊(duì)列”里剩下一個(gè) setTimeout 的回調(diào)函數(shù),其被送入主線程執(zhí)行棧
第三輪事件循環(huán)宏任務(wù)
開始執(zhí)行第二個(gè) setTimeout 回調(diào)函數(shù)
遇到 console.log 輸出 8
遇到 new Promise 其回調(diào)函數(shù)作為同步任務(wù)直接執(zhí)行
遇到 console.log 輸出 9
遇到 then 回調(diào)函數(shù)作為異步任務(wù)進(jìn)入“微任務(wù)隊(duì)列”
此時(shí)第三輪事件循環(huán)宏任務(wù)結(jié)束,依次輸出 8 9
第三輪事件循環(huán)微任務(wù)
執(zhí)行注冊(cè)在“微任務(wù)隊(duì)列”里的微任務(wù),遇到 then 執(zhí)行其回調(diào)輸出 10
此時(shí)第三輪事件循環(huán)微任務(wù)結(jié)束,輸出 10
第三輪事件循環(huán)結(jié)束,此時(shí)“微任務(wù)隊(duì)列”已被清空,“宏任務(wù)隊(duì)列”已被清空
至此整段代碼執(zhí)行完畢,完整輸出結(jié)果為:0 1 6 11 2 7 3 4 5 8 9 10
Node.JS 環(huán)境Node.JS 環(huán)境下任務(wù)隊(duì)列有層級(jí)之分,按層級(jí)執(zhí)行任務(wù)隊(duì)列
第一輪事件循環(huán)宏任務(wù)
開始執(zhí)行代碼,遇到 console.log 輸出 0
接著遇到 new Promise 其回調(diào)函數(shù)作為同步任務(wù)直接執(zhí)行
遇到 console.log 輸出 1
遇到 then 回調(diào)函數(shù)作為異步任務(wù)進(jìn)入“微任務(wù)隊(duì)列”
接著遇到 setTimeout 其回調(diào)函數(shù)注冊(cè)到 timer 階段
接著遇到 new Promise 其回調(diào)函數(shù)作為同步任務(wù)直接執(zhí)行
遇到 console.log 輸出 6
遇到 then 回調(diào)函數(shù)作為異步任務(wù)進(jìn)入“微任務(wù)隊(duì)列”
接著遇到 setTimeout 其回調(diào)函數(shù)注冊(cè)到 timer 階段
最后遇到 console.log 輸出 11
此時(shí)第一輪事件循環(huán)宏任務(wù)結(jié)束,依次輸出 0 1 6 11
第一輪事件循環(huán)微任務(wù)
執(zhí)行注冊(cè)在“微任務(wù)隊(duì)列”里的微任務(wù),遇到 then 執(zhí)行其回調(diào)輸出 2
遇到 then 執(zhí)行其回調(diào)輸出 7
此時(shí)第一輪事件循環(huán)微任務(wù)結(jié)束,依次輸出 2 7
第一輪事件循環(huán)結(jié)束,此時(shí)“微任務(wù)隊(duì)列”已被清空,timer 隊(duì)列里有兩個(gè) setTimeout 的回調(diào)函數(shù)
第二輪事件循環(huán) timer 階段
執(zhí)行第一個(gè) setTimeout 回調(diào)函數(shù)
遇到 console.log 輸出 3
遇到 new Promise 其回調(diào)函數(shù)作為同步任務(wù)直接執(zhí)行
遇到 console.log 輸出 4
遇到 then 回調(diào)函數(shù)作為異步任務(wù)進(jìn)入“微任務(wù)隊(duì)列”
執(zhí)行第二個(gè) setTimeout 回調(diào)函數(shù)
遇到 console.log 輸出 8
遇到 new Promise 其回調(diào)函數(shù)作為同步任務(wù)直接執(zhí)行
遇到 console.log 輸出 9
遇到 then 回調(diào)函數(shù)作為異步任務(wù)進(jìn)入“微任務(wù)隊(duì)列”
此時(shí)第二輪事件循 timer 階段結(jié)束,依次輸出 3 4 8 9
第二輪事件循環(huán) timer 階段微任務(wù)
執(zhí)行注冊(cè)在“微任務(wù)隊(duì)列”里的微任務(wù)
遇到 then 執(zhí)行其回調(diào)輸出 5
遇到 then 執(zhí)行其回調(diào)輸出 10
此時(shí)第二輪事件循環(huán) timer 階段微任務(wù)結(jié)束,輸出 5 10
第二輪事件循環(huán)結(jié)束,至此整段代碼執(zhí)行完畢,完整輸出結(jié)果為:0 1 6 11 2 7 3 4 8 9 5 10
參考文章阮一峰 - JavaScript 運(yùn)行機(jī)制詳解:再談Event Loop阮一峰 - Node 定時(shí)器詳解
Philip Roberts - Help,I’m stuck in an event loop
lynnelv - 深入理解js事件循環(huán)機(jī)制(Node.js篇)
這一次,徹底弄懂 JavaScript 執(zhí)行機(jī)制
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/101469.html
摘要:知識(shí)點(diǎn)聲明的變量在預(yù)解析的時(shí)候只執(zhí)行聲明,不會(huì)執(zhí)行定義,默認(rèn)值是。聲明的函數(shù)在預(yù)解析的時(shí)候會(huì)提前聲明并且會(huì)同時(shí)定義。 showImg(https://segmentfault.com/img/bVbnY76?w=2500&h=1250); 知識(shí)點(diǎn) var 聲明的變量在預(yù)解析的時(shí)候只執(zhí)行聲明,不會(huì)執(zhí)行定義,默認(rèn)值是 undefined。 function 聲明的函數(shù)在預(yù)解析的時(shí)候會(huì)...
摘要:哪吒社區(qū)技能樹打卡打卡貼函數(shù)式接口簡介領(lǐng)域優(yōu)質(zhì)創(chuàng)作者哪吒公眾號(hào)作者架構(gòu)師奮斗者掃描主頁左側(cè)二維碼,加入群聊,一起學(xué)習(xí)一起進(jìn)步歡迎點(diǎn)贊收藏留言前情提要無意間聽到領(lǐng)導(dǎo)們的談話,現(xiàn)在公司的現(xiàn)狀是碼農(nóng)太多,但能獨(dú)立帶隊(duì)的人太少,簡而言之,不缺干 ? 哪吒社區(qū)Java技能樹打卡?【打卡貼 day2...
摘要:歡迎來我的個(gè)人站點(diǎn)性能優(yōu)化其他優(yōu)化瀏覽器關(guān)鍵渲染路徑開啟性能優(yōu)化之旅高性能滾動(dòng)及頁面渲染優(yōu)化理論寫法對(duì)壓縮率的影響唯快不破應(yīng)用的個(gè)優(yōu)化步驟進(jìn)階鵝廠大神用直出實(shí)現(xiàn)網(wǎng)頁瞬開緩存網(wǎng)頁性能管理詳解寫給后端程序員的緩存原理介紹年底補(bǔ)課緩存機(jī)制優(yōu)化動(dòng) 歡迎來我的個(gè)人站點(diǎn) 性能優(yōu)化 其他 優(yōu)化瀏覽器關(guān)鍵渲染路徑 - 開啟性能優(yōu)化之旅 高性能滾動(dòng) scroll 及頁面渲染優(yōu)化 理論 | HTML寫法...
摘要:歡迎來我的個(gè)人站點(diǎn)性能優(yōu)化其他優(yōu)化瀏覽器關(guān)鍵渲染路徑開啟性能優(yōu)化之旅高性能滾動(dòng)及頁面渲染優(yōu)化理論寫法對(duì)壓縮率的影響唯快不破應(yīng)用的個(gè)優(yōu)化步驟進(jìn)階鵝廠大神用直出實(shí)現(xiàn)網(wǎng)頁瞬開緩存網(wǎng)頁性能管理詳解寫給后端程序員的緩存原理介紹年底補(bǔ)課緩存機(jī)制優(yōu)化動(dòng) 歡迎來我的個(gè)人站點(diǎn) 性能優(yōu)化 其他 優(yōu)化瀏覽器關(guān)鍵渲染路徑 - 開啟性能優(yōu)化之旅 高性能滾動(dòng) scroll 及頁面渲染優(yōu)化 理論 | HTML寫法...
閱讀 1997·2021-11-25 09:43
閱讀 1481·2021-11-22 14:56
閱讀 3343·2021-11-22 09:34
閱讀 2108·2021-11-15 11:37
閱讀 2384·2021-09-01 10:46
閱讀 1467·2019-08-30 15:44
閱讀 2356·2019-08-30 13:15
閱讀 2451·2019-08-29 13:07