前言
最近一直在看基本知識點,發(fā)現(xiàn)自己的理解太過于偏頗、淺顯加之記憶力不行,遂還是以文字的形式記錄下來,這是緣由之一。當然,也希望通過這樣的方式去整理邏輯,使之清晰。
為什么會有event loop?
和其他語言相比較,JavaScript語言特點就是單線程的。起初這門語言的設(shè)計主要用于與用戶互動的,提高用戶網(wǎng)頁使用的體驗度。然后漸漸發(fā)展成前端開發(fā)必不可少的語言。但是在實際的業(yè)務當中,開發(fā)人員發(fā)現(xiàn)單線程有太多的阻礙。這也是event loop產(chǎn)生的原因。
什么是event loop
主線程從“任務隊列”中讀取事件,這個過程是循環(huán)不斷的,所以整個的這種運行機制成為even loop。
運行進制
首先需要了解JavaScript的運行機制
- 所有同步任務都在主線程上執(zhí)行,形成一個執(zhí)行棧。
- 主線程之外,還存在“任務隊列”。只要異步任務有了運行結(jié)果,就在“任務隊列”之中放置一個事件。
- 一旦“執(zhí)行?!敝械乃型饺蝿請?zhí)行完畢,系統(tǒng)就會讀取“任務隊列”,看看里面有哪些事件。那些對應的異步任務,于是接收等待狀態(tài),進入執(zhí)行棧,開始執(zhí)行。
- 主線任務不斷重復上面的第三步。
再說even loop 運行進制。
主線任務會不斷從任務隊列中按順序去取任務執(zhí)行,每執(zhí)行完一個任務都會檢查microtask隊列是否為空,如果不為空則會一次性執(zhí)行完所有的microtask。然后再進入下一個循環(huán)去任務隊列中去下一個任務執(zhí)行。
具體如下:
- 當選擇需要執(zhí)行的宏任務隊列,選擇一個最先進入任務隊列的宏任務,如果沒有宏任務選擇,則會跳轉(zhuǎn)至microtask的執(zhí)行步驟。
- 將事件循環(huán)的當前運行宏任務設(shè)置為已選擇的宏任務。
- 運行宏任務。
- 將事件循環(huán)的當前運行任務設(shè)置為null。
- 將運行完的宏任務從宏任務隊列中移除。
- microtask步驟:進入microtask檢查點。
- 更新界面渲染。
- 返回第一步
執(zhí)行進入microtask 檢查的具體步驟:
- 設(shè)置進入microtask檢查點的標志位true
- 當事件循環(huán)的微任務隊列不為空時,選擇一個最先進入microtask隊列的microtask;設(shè)置事件循環(huán)的當前運行任務為null,將運行結(jié)束的microtask從microtask隊列中移除。
- 對于相應事件循環(huán)的每個環(huán)境設(shè)置對象,通知他們哪些promise為rejected.
- 清理indexDB的事務。
- 設(shè)置進入microtask檢查點的標志為false。
console.log('script start')
setTimeout(function(){
console.log('setTimeout --- 0')
},0)
setTimeout(function(){
console.log('setTimeout --- 200')
setTimeout(function(){
console.log('inner -setTimeout --- 0')
})
Promise.resolve().then(function(){
console.log('promise5')
})
},200)
Promise.resolve().then(function(){
console.log('promise1')
}).then(function(){
console.log('promise2')
})
Promise.resolve().then(function(){
console.log('promise3')
})
console.log('script end')
運行結(jié)果:
script start
script end
promise1
promise3
promise2
setTimeout --- 0
setTimeout --- 200
promise5
inner -setTimeout --- 0
分析:
- 首先順序執(zhí)行完主進程上的同步任務,第一句和最后一句的console.log
- 接著遇到setTimeout 0,它的作用是在 0ms 后將回調(diào)函數(shù)放到宏任務隊列中(這個任務在下一次的事件循環(huán)中執(zhí)行)。
- 接著遇到setTimeout 200,它的作用是在 200ms 后將回調(diào)函數(shù)放到宏任務隊列中(這個任務在再下一次的事件循環(huán)中執(zhí)行)。
- 同步任務執(zhí)行完之后,首先檢查微任務隊列, 即 microtask隊列,發(fā)現(xiàn)此隊列不為空,執(zhí)行第一個promise的then回調(diào),輸出 'promise1',然后執(zhí)行第二個promise的then回調(diào),輸出'promise3',由于第一個promise的.then()的返回依然是promise,所以第二個.then()會放到microtask隊列繼續(xù)執(zhí)行,輸出 'promise2';
5.此時microtask隊列為空,進入下一個事件循環(huán), 檢查宏任務隊列,發(fā)現(xiàn)有 setTimeout的回調(diào)函數(shù),立即執(zhí)行回調(diào)函數(shù)輸出 'setTimeout---0',檢查microtask 隊列,隊列為空,進入下一次事件循環(huán). - 檢查宏任務隊列,發(fā)現(xiàn)有 setTimeout的回調(diào)函數(shù), 立即執(zhí)行回調(diào)函數(shù)輸出'setTimeout---200'
- 接著遇到setTimeout 0,它的作用是在 0ms 后將回調(diào)函數(shù)放到宏任務隊列中,檢查微任務隊列,即 microtask 隊列,發(fā)現(xiàn)此隊列不為空,執(zhí)行promise的then回調(diào),輸出'promise5'。
- 此時microtask隊列為空,進入下一個事件循環(huán),檢查宏任務隊列,發(fā)現(xiàn)有 setTimeout 的回調(diào)函數(shù),立即執(zhí)行回調(diào)函數(shù)輸出,輸出'inner-setTimeout---0'。代碼執(zhí)行結(jié)束。