Web Workers
爲了使瀏覽器這執行js代碼時,不阻塞進程,所以HTML5制定了Web Workers標準,允許js創建多個線程,但子線程收到主線程的控制,並且不能對DOM進行操作,以防止多個線程同時對一處DOM進行不同操作,這樣可以避免“一核有難,七核圍觀”。
爲什麼JS需要異步
其根本原因在於計算機對不同硬件訪問速度的差異,其速度快到慢依次排序爲:寄存器、L1高速緩存、L2高速緩存、L3高速緩存、內存、硬盤、Web服務器
JS如何實現異步
負責解釋和執行JavaScript代碼的線程只有一個,但瀏覽器內部還有其他線程專門負責異步任務。例如:定時器、UI、事件、網絡之類專門的線程來負責相關任務處理。
JavaScript主線程會維護一個執行棧(先進後出),然後逐行執行。當遇到了異步代碼(例如Promise.then、AJAX、setTimeout等),那主線程會將這部分代碼交給上述的專門的線程去執行,然後跳過這些代碼繼續向下執行,知道當前代碼段結束。
與此同時,瀏覽器的其他線程處理好了主線程交給其的異步代碼執行前的準備工作的時候(例如AJAX數據,鼠標事件觸發,定時器的時間到了等),瀏覽器會將當前的異步代碼放入一個異步任務隊列(先進先出)
每當主線程執行完當前代碼,都會檢查異步隊列是否有任務需要執行,如果有的話,就將那個任務逐行執行,當在裏面遇到了異步代碼,就再交給瀏覽器的其他線程去處理,然後跳過這些代碼繼續執行,知道當前代碼段結束,然後再檢查異步隊列,如此循環往復,所以這個過程被稱爲事件循環。
異步隊列中的微任務和宏任務
異步隊列並非只有只有一個,其主要可被分爲微任務隊列與宏任務隊列
微任務隊列:process.nextTick、Promise、Object.observe、MutationObserver
宏任務隊列:setTimeout、setInterval、setMMediate、I/O、UI渲染、<script>中的js代碼
兩個隊列都是依次執行,但是有一個很重要的區別:
當前的微任務隊列中的所有任務都會被全部執行完畢,但宏隊列中的任務,則只會執行最先的任務,然後就跳到了微任務隊列中。
任務的優先級大體是(此順序爲《深入淺出Javascript》中所提出的):
idle觀察者 > I/O觀察者 > check觀察者
process.nextTick(idle) > Promise(原生實現) setTimeout(I/O) > setImmediate (check)
setImmediate(function(){
console.log(1)
},0)
setTimeout(function(){
console.log(2)
},0)
new Promise(function(resolve){
console.log(3)
resolve()
console.log(4)
}).then(function(){
console.log(5)
})
console.log(6)
process.nextTick(function(){
console.log(7)
})
console.log(8)
執行結果是
3
4
6
8
7
5
2
1