?閑話少述,直切主題
js 的事件循環(huán)(event loop)的基本概念
JavaScript 有一個(gè)基于事件循環(huán)的并發(fā)模型,事件循環(huán)負(fù)責(zé)執(zhí)行代碼、收集和處理事件以及執(zhí)行隊(duì)列中的子任務(wù)。
簡單點(diǎn)說,就是js 在處理任務(wù)中會(huì)有一套整理 收集 排隊(duì) 這些任務(wù)的機(jī)制 這種機(jī)制我們就叫做事件循環(huán)機(jī)制
首先,js是單進(jìn)程的的,主要任務(wù)是用來處理用戶的交互,用戶與瀏覽器的交互無非就是響應(yīng)DOM的增刪改。
JS 單線程指的是 javascript 引擎(如V8)在同一時(shí)刻只能處理一個(gè)任務(wù)。
或許有人會(huì)問js 是單進(jìn)程,異步任務(wù) ajax 難道不是可以和 JS 代碼同時(shí)執(zhí)行么?
從這個(gè)問題出發(fā)我們,我們現(xiàn)在具體來看看js 的事件循環(huán)機(jī)制


我們來看看這段代碼 控制臺(tái)的打印順序
1.先打印了fn()函數(shù)的log
2.打印log('主任務(wù)1')
3.打印promise 非回調(diào)函數(shù)執(zhí)行的代碼塊
4.打印log('主任務(wù)2')
5.打印promise 成功回調(diào)執(zhí)行的代碼塊
6.打印settimeOut
那么這樣的打印方法背后到底做了什么?
首先 我們需要了解幾個(gè)概念 方便我們知道到底整個(gè)代碼是怎樣執(zhí)行的?
概念1:瀏覽器的內(nèi)核是多線程的,它們?cè)趦?nèi)核制控下相互配合以保持同步,一個(gè)瀏覽器至少實(shí)現(xiàn)三個(gè)常駐線程:javascript引擎線程,GUI渲染線程,瀏覽器事件觸發(fā)線程。
概念2:?
主任務(wù): 一般是主代碼塊執(zhí)行的代碼 同步代碼
微任務(wù) process.nextTick promise Object.observe MutationObserver
宏任務(wù): script setTimeout setInterval setImmediate I/O UI rendering
那有了這兩個(gè)概念 ,現(xiàn)在我來畫一張圖 告訴大家 整個(gè)代碼是整樣往下走的

由上圖所知的規(guī)則 我們來分析一下 我們所寫的那段簡單的代碼
1.js 在執(zhí)行的時(shí)候 先是收集 整理任務(wù)類型 分清楚 主任務(wù) 宏任務(wù) 微任務(wù)
這個(gè)時(shí)候?事件觸發(fā)線程 就很重要了 他的作用主要是在定時(shí)觸發(fā)器線程、異步HTTP請(qǐng)求線程滿足特定條件下的回調(diào)函數(shù)push到事件隊(duì)列中,等待js引擎空閑的時(shí)候去執(zhí)行,
2.在一次事件循環(huán)中 先執(zhí)行主任務(wù)棧中的相關(guān)任務(wù) 所以我們先打印了fn()函數(shù)的log
3.執(zhí)行到setTimeout的時(shí)候 觸發(fā)事件觸發(fā)線程?事件觸發(fā)線程就將他的回調(diào)函數(shù)push 到宏任務(wù)執(zhí)行棧中 也就是宏任務(wù)隊(duì)列
4.執(zhí)行到Promise的時(shí)候?Promise的回調(diào)函數(shù)會(huì)被相對(duì)應(yīng)的放進(jìn)宏任務(wù)的執(zhí)行棧中 等待執(zhí)行
所以正確的一次 Event loop 順序是這樣的
1.執(zhí)行同步代碼,也就是主任務(wù)代碼
2.執(zhí)行棧為空,查詢是否有微任務(wù)需要執(zhí)行
3.執(zhí)行所有微任務(wù)
4.必要的話渲染 UI
5.然后開始下一輪?Event loop,執(zhí)行宏任務(wù)中的異步代碼