事件循環(huán)Event Loop

一句話解釋

js的主線程是單線程運(yùn)行的,主線程先執(zhí)行同步代碼,遇到異步操作如ajax請(qǐng)求、定時(shí)器等需要在一段時(shí)間后再執(zhí)行的事件,便交給其他線程如請(qǐng)求線程、定時(shí)觸發(fā)線程,滿足條件后就放到任務(wù)隊(duì)列中,主線程執(zhí)行完任務(wù)后,就去任務(wù)隊(duì)列里拉取任務(wù),按照先進(jìn)先出的原則依次執(zhí)行,如此循環(huán);
其中任務(wù)隊(duì)列分為宏任務(wù)隊(duì)列與微任務(wù)隊(duì)列,執(zhí)行完一個(gè)宏任務(wù)都會(huì)去檢查是否有微任務(wù),執(zhí)行完所有微任務(wù)后,頁面更新渲染,再執(zhí)行下一個(gè)宏任務(wù);

問答

1.為什么JS是單線程的?

  • 防止同時(shí)操作同一DOM出現(xiàn)混亂。
  • 所謂的"JS是單線程的"只是指JS的主運(yùn)行線程只有一個(gè),而不是整個(gè)運(yùn)行環(huán)境都是單線程。

2.JS單線程如何實(shí)現(xiàn)異步?

  • JS異步的實(shí)現(xiàn)靠的就是瀏覽器的多線程,當(dāng)遇到異步API時(shí),就將這個(gè)任務(wù)交給對(duì)應(yīng)的線程,當(dāng)這個(gè)異步API滿足回調(diào)條件時(shí),對(duì)應(yīng)的線程又通過事件觸發(fā)線程將這個(gè)事件放入任務(wù)隊(duì)列,然后主線程從任務(wù)隊(duì)列取出事件繼續(xù)執(zhí)行。
  • JS引擎線程跟GUI線程是互斥的,一個(gè)執(zhí)行就另一個(gè)不執(zhí)行,如果JS長時(shí)間運(yùn)行,GUI線程就不能執(zhí)行,整個(gè)頁面就感覺卡死了。

3.為什么要有微任務(wù)?

  • 微任務(wù)是為了解決效率和實(shí)時(shí)性問題,處理高優(yōu)先級(jí)的任務(wù)。

4.常見的宏任務(wù)與微任務(wù)有哪些?

  • 宏任務(wù)macrotask:script(整體代碼)、setTimeout、setInterval、setImmediate、I/O
  • 微任務(wù)microtask:Promise、MutaionObserver、process.nextTick(Node)

5.瀏覽器什么時(shí)候渲染?

  • 執(zhí)行完一個(gè)宏任務(wù)以及當(dāng)前所有微任務(wù)后渲染一次;
  • 但并不是每輪event loop都會(huì)更新渲染,這取決于是否修改了dom和瀏覽器覺得是否有必要在此時(shí)立即將新狀態(tài)呈現(xiàn)給用戶。如果在一幀的時(shí)間內(nèi)(時(shí)間并不確定,因?yàn)闉g覽器每秒的幀數(shù)總在波動(dòng),16.7ms只是估算并不準(zhǔn)確)修改了多處dom,瀏覽器可能將變動(dòng)積攢起來,只進(jìn)行一次繪制。

6.與Node環(huán)境的事件循環(huán)有什么區(qū)別?

瀏覽器和Node 環(huán)境下,microtask 任務(wù)隊(duì)列的執(zhí)行時(shí)機(jī)不同:

  • 瀏覽器端,microtask 在事件循環(huán)的 macrotask 執(zhí)行完之后執(zhí)行;
  • Node版本>=11,和瀏覽器表現(xiàn)一致;
  • Node版本<11,microtask 在事件循環(huán)的各個(gè)階段之間執(zhí)行,也就是一個(gè)階段執(zhí)行完畢,就會(huì)去執(zhí)行microtask隊(duì)列的任務(wù);

由于定時(shí)器插入時(shí)機(jī)不同,同一代碼執(zhí)行結(jié)果也會(huì)不同,如下:

setTimeout(()=>{
    console.log('timer1')
    Promise.resolve().then(function() {
        console.log('promise1')
    })
}, 0)
setTimeout(()=>{
    console.log('timer2')
    Promise.resolve().then(function() {
        console.log('promise2')
    })
}, 0)

Node端分為6個(gè)階段,

  • timers 階段:這個(gè)階段執(zhí)行timer(setTimeout、setInterval)的回調(diào)
  • I/O callbacks 階段:處理一些上一輪循環(huán)中的少數(shù)未執(zhí)行的 I/O 回調(diào)
  • idle, prepare 階段:僅node內(nèi)部使用
  • poll 階段:獲取新的I/O事件, 適當(dāng)?shù)臈l件下node將阻塞在這里
  • check 階段:執(zhí)行 setImmediate() 的回調(diào)
  • close callbacks 階段:執(zhí)行 socket 的 close 事件回調(diào)
    外部輸入數(shù)據(jù)-->輪詢階段(poll)-->檢查階段(check)-->關(guān)閉事件回調(diào)階段(close callback)-->定時(shí)器檢測(cè)階段(timer)-->I/O事件回調(diào)階段(I/O callbacks)-->閑置階段(idle, prepare)-->輪詢階段(按照該順序反復(fù)運(yùn)行)

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容