為什么有事件循環(huán)
-JS單線程
JavaScript設(shè)計(jì)之初就是單線程的,主要用途是與用戶互動(dòng),以及操作DOM。為了避免兩個(gè)線程同時(shí)操作DOM的情況發(fā)生,就采用了單線程。即使H5提出了web worker標(biāo)準(zhǔn),它有很多限制,受主線程控制,是主線程的子線程。
-非阻塞
通過EventLoop實(shí)現(xiàn)
宏任務(wù)和微任務(wù)
宏任務(wù)和微任務(wù)
為什么要引入微任務(wù),只有一種類型的任務(wù)不行么?
頁面渲染事件,各種IO的完成事件等隨時(shí)被添加到任務(wù)隊(duì)列中,一直會(huì)保持先進(jìn)先出的原則執(zhí)行,我們不能準(zhǔn)確地控制這些事件被添加到任務(wù)隊(duì)列中的位置。但是這個(gè)時(shí)候突然有高優(yōu)先級的任務(wù)需要盡快執(zhí)行,那么一種類型的任務(wù)就不合適了,所以引入了微任務(wù)隊(duì)列。
瀏覽器里的事件循環(huán)
關(guān)于微任務(wù)和宏任務(wù)在瀏覽器的執(zhí)行順序是這樣的:
執(zhí)行一只task(宏任務(wù))
執(zhí)行完micro-task隊(duì)列 (微任務(wù))
如此循環(huán)往復(fù)下去
常見的 task(宏任務(wù)) 比如:setTimeout、setInterval、script(整體代碼)、 I/O 操作、UI 渲染等。
常見的 micro-task 比如: new Promise().then(回調(diào))、MutationObserver(html5新特性) 等。
Node里的事件循環(huán)
大體的task(宏任務(wù))執(zhí)行順序是這樣的:
timers定時(shí)器:本階段執(zhí)行已經(jīng)安排的 setTimeout() 和 setInterval() 的回調(diào)函數(shù)。
pending callbacks待定回調(diào):執(zhí)行延遲到下一個(gè)循環(huán)迭代的 I/O 回調(diào)。
idle, prepare:僅系統(tǒng)內(nèi)部使用。
poll 輪詢:檢索新的 I/O 事件;執(zhí)行與 I/O 相關(guān)的回調(diào)(幾乎所有情況下,除了關(guān)閉的回調(diào)函數(shù),它們由計(jì)時(shí)器和 setImmediate() 排定的之外),其余情況 node 將在此處阻塞。
check 檢測:setImmediate() 回調(diào)函數(shù)在這里執(zhí)行。
close callbacks 關(guān)閉的回調(diào)函數(shù):一些準(zhǔn)備關(guān)閉的回調(diào)函數(shù),如:socket.on('close', ...)。
微任務(wù)和宏任務(wù)在Node的執(zhí)行順序
Node 10以前:
執(zhí)行完一個(gè)階段的所有任務(wù)
執(zhí)行完nextTick隊(duì)列里面的內(nèi)容
然后執(zhí)行完微任務(wù)隊(duì)列的內(nèi)容
Node 11以后:
和瀏覽器的行為統(tǒng)一了,都是每執(zhí)行一個(gè)宏任務(wù)就執(zhí)行完微任務(wù)隊(duì)列。