瀏覽器中的 Event loop
JavaScript 是單線程的
? ?????首先,語言產(chǎn)生的時代多進程多線程的架構(gòu)并不普及誓禁,基于當時硬件支持也不好鲜屏,而且 多線程比較復(fù)雜俺陋,多線程操作需要加鎖,使得編碼方面就會變得很復(fù)雜考廉;而且當時 JavaScript?只是處理頁面的用戶交互秘豹,以及操作 DOM 和 CSS,如果是多線程昌粤,兩個線程 同時操作 同一個 DOM既绕, 就會產(chǎn)生 預(yù)期的結(jié)果,所以 JavaScript?選擇單線程婚苹。
基本概念
? ? JavaScript 分為 同步任務(wù) 和 異步任務(wù)岸更;
? ? 所有同步任務(wù)都在??main thread?主線程?上執(zhí)行,形成一個?執(zhí)行棧(execution context stack)膊升,所有的任務(wù)都會被放到執(zhí)行棧等待主線程執(zhí)行怎炊;
? ? 執(zhí)行棧?采用的是?后進先出?的規(guī)則,當函數(shù)執(zhí)行的時候廓译,會被添加到棧的頂部评肆,當執(zhí)行棧執(zhí)行完成后,就會從棧頂移出非区,直到棧內(nèi)被清空瓜挽;
? ?主線程之外,存在一個任務(wù)隊列(task queue)征绸,在走主流程的時候久橙,如果碰到異步任務(wù)俄占,那么就在?任務(wù)隊列?中放置這個異步任務(wù);
????一旦?執(zhí)行棧?中所有?同步任務(wù)執(zhí)行完畢淆衷,系統(tǒng)就會讀取?任務(wù)隊列缸榄,看看里面存在哪些事件。那些對應(yīng)的異步任務(wù) 在異步任務(wù)有了結(jié)果后祝拯,將注冊的回調(diào)函數(shù)放入?任務(wù)隊列?中等待主線程空閑的時候(調(diào)用棧被清空)甚带,被讀取到棧內(nèi)等待主線程的執(zhí)行。
? ?任務(wù)隊列 Task Queue佳头,即隊列鹰贵,是一種先進先出的一種數(shù)據(jù)結(jié)構(gòu)。
宏任務(wù)和微任務(wù)
? ?????在 JavaScript 中康嘉,任務(wù)被分為兩種碉输,一種 宏任務(wù)(MacroTask),一種叫 微任務(wù)(MicroTask)凄鼻。
? ?????MacroTask(宏任務(wù))有:setTimeout腊瑟、setInterval、setImmediate块蚌、I/O闰非、UI Rendering
? ?????MicroTask(微任務(wù))有:Process.nextTick(Node獨有)、Promise峭范、Object.observe(廢棄)财松、MutationObserver
? ?? ??微任務(wù)和宏任務(wù)是綁定的,每個宏任務(wù)在執(zhí)行時纱控,會創(chuàng)建自己的微任務(wù)隊列辆毡。
? ??? ?宏任務(wù)結(jié)束后,會執(zhí)行渲染甜害,然后執(zhí)行下一個宏任務(wù)舶掖, 而微任務(wù)可以理解成在當前宏任務(wù)執(zhí)行后立即執(zhí)行的任務(wù)。
? ? ? ? 宏任務(wù) --> 微任務(wù) --> 渲染 --> 宏任務(wù) --> ......
? ???????微任務(wù)就是一個需要異步執(zhí)行的函數(shù)尔店,執(zhí)行時機是在主函數(shù)執(zhí)行結(jié)束之后眨攘、當前宏任務(wù)結(jié)束之前。?
? ??????當JavaScript執(zhí)??段腳本的時候嚣州,V8會為其創(chuàng)建?個全局執(zhí)行上下文鲫售,在創(chuàng)建全局執(zhí)行上下文的 同時,V8引擎也會在內(nèi)部創(chuàng)建?個?微任務(wù)隊列 來存放微任務(wù)该肴;
????????在當前宏任務(wù)執(zhí)行的過程中情竹,有時候會產(chǎn)生多個微任務(wù);如果在執(zhí)行微任務(wù)的過程中匀哄,產(chǎn)?了新的微任務(wù)秦效,同樣會將該微任務(wù)添加到微任務(wù)隊列中雏蛮,V8引擎?直循環(huán)執(zhí)行微任務(wù)隊列中的任務(wù),直到隊列為空才算執(zhí)行結(jié)束棉安,并不會推遲到下個宏任務(wù)中執(zhí)行底扳。
? ? ? ?總結(jié) :微任務(wù)和宏任務(wù)是綁定的,每個宏任務(wù)在執(zhí)行時贡耽,會創(chuàng)建自己的微任務(wù)隊列。
瀏覽器事件循環(huán)的進程模型
????執(zhí)行棧在執(zhí)行完?同步任務(wù)?后鹊汛,查看?執(zhí)行棧?是否為空蒲赂,如果執(zhí)行棧為空,就會去檢查?微任務(wù)?(microTask)隊列是否為空刁憋,如果為空的話滥嘴,就執(zhí)行 Task(宏任務(wù)),否則就一次性執(zhí)行完所有微任務(wù)至耻。
????每次單個宏任務(wù)?執(zhí)行完畢后若皱,檢查?微任務(wù)?(microTask) 隊列是否為空,如果不為空的話尘颓,會按照?先入先出 的規(guī)則全部執(zhí)行完微任務(wù)(microTask)后走触,設(shè)置?微任務(wù)(microTask)隊列為null,然后再執(zhí)行?宏任務(wù)疤苹,如此循環(huán)互广。
? ? 注意 :async/await 在底層轉(zhuǎn)換成了 promise 和 then 回調(diào)函數(shù),也就是說卧土,這是 promise 的語法糖惫皱。
? ??sync函數(shù)在await之前的代碼都是同步執(zhí)行的,可以理解為await之前的代碼屬于new Promise時傳入的代碼尤莺,await之后的所有代碼都是在Promise.then中的回調(diào)
? ? 總結(jié)
? ??每當一個 js 腳本運行的時候旅敷,都會先執(zhí)行script中的整體代碼;
????當執(zhí)行棧中的同步任務(wù)執(zhí)行完畢颤霎,就會執(zhí)行?微任務(wù)?中的第一個任務(wù)并推入執(zhí)行棧執(zhí)行媳谁,當執(zhí)行棧為空,則再次讀取執(zhí)行微任務(wù)捷绑,循環(huán)重復(fù)直到微任務(wù)列表為空韩脑;
? ??等到微任務(wù)列表為空,才會讀取宏任務(wù)中的第一個任務(wù)并推入執(zhí)行棧執(zhí)行粹污,當執(zhí)行棧為空則再讀取執(zhí)行微任務(wù)段多,微任務(wù)為空才再讀取執(zhí)行宏任務(wù),如此循環(huán)壮吩。
Nodejs 中的 Event Loop
? ? ? ?Node 中的 Event Loop 和瀏覽器中的是完全不相同的東西
? ? nodejs 的 event loop 分為 6 個階段进苍,每當進入某一個階段的時候加缘,都會從對應(yīng)的回調(diào)隊列中取出函數(shù)去執(zhí)行。當隊列為空或者執(zhí)行的回調(diào)函數(shù)數(shù)量到達系統(tǒng)設(shè)定的閾值觉啊,就會進入下一階段拣宏。
? ??????timers 階段:這個階段執(zhí)行timer(setTimeout、setInterval)的回調(diào)
? ??????I/O callbacks 階段:執(zhí)行一些系統(tǒng)調(diào)用錯誤杠人,比如網(wǎng)絡(luò)通信的錯誤回調(diào)
? ??????idle, prepare 階段:僅node內(nèi)部使用
? ??????poll 階段:獲取新的I/O事件, 適當?shù)臈l件下node將阻塞在這里
? ??????check 階段:執(zhí)行?setImmediate()?的回調(diào)
? ??????close callbacks 階段:執(zhí)行?socket?的?close?事件回調(diào)
? ? 執(zhí)行順序
? ? ? ??一開始執(zhí)行棧的同步任務(wù)(宏任務(wù))執(zhí)行完畢后(將異步任務(wù)放入對應(yīng)的隊列)勋乾,再會先去執(zhí)行微任務(wù)(這點跟瀏覽器端的一樣, 這個過程中 先執(zhí)行?process.nextTick嗡善, 在執(zhí)行其他 微任務(wù)辑莫,process.nextTick 優(yōu)先級較高),接著進入 event loop 的 六個階段罩引,循環(huán)執(zhí)行各吨。
? ? ? ? 詳細的可以參看文檔?事件循環(huán)
????process.nextTick(微任務(wù))
????????這個函數(shù)其實是獨立于 Event Loop 之外的,它有一個自己的隊列袁铐,當每個階段完成后揭蜒,如果存在 nextTick 隊列,就會清空隊列中的所有回調(diào)函數(shù)剔桨,并且優(yōu)先于其他 microtask 執(zhí)行屉更。
? ? 瀏覽器 Event loop 和 nodejs Event loop 差異
? ??????瀏覽器環(huán)境下,microtask的任務(wù)隊列是每個macrotask執(zhí)行完之后執(zhí)行领炫。
? ??????在Node.js中偶垮,microtask 會在事件循環(huán)的各個階段之間執(zhí)行,也就是一個階段執(zhí)行完畢帝洪,就會去執(zhí)行microtask隊列的任務(wù)似舵。