這是本人的理解,並且Google browser斷點實踐的,不同瀏覽器可能存在差異。章末附帶外文視訊地址。
JS特點
javascript一門單執行緒的非阻塞的指令碼語言。當使用者進行點選元素,又進行元素移除,這樣就會導致哪個事件的優先順序不知道,所以JS必須是單執行緒的。為了協調事件,使用者互動,指令碼,UI渲染,和網路處理,Event Loop可以防止主執行緒阻塞。在某些花費時間較長的事件,瀏覽器會將它們掛起(pending),事件等待被執行,事件迴圈是通過任務佇列實現的。
one thread == one callback == one thing at a time.
Event Loop
- Browing Context
- Worker 兩者是獨立的,有各自的執行緒環境。
棧(Stack)
棧是一種遵循後進先出(LIFO)的資料集合,新新增或待刪除的元素都儲存在棧的末尾,稱作棧頂,另一端稱作棧底。在棧裡,新元素都靠近棧頂,舊元素都靠近棧底
佇列(Queue)
佇列是一種遵循先進先出(FIFO)的資料集合,新的條目會被加到佇列的末尾,舊的條目會從佇列的頭部被移出。
微任務與任務
- 任務task:script(整體程式碼)、setTimeout、setInterval、I/O(輸入輸出裝置)、UI互動事件,setImmediate(Node.js環境),webAPI callback()等
- 微任務:Promise、MutationObserver、process.nextTick(Node.js環境)
JS執行規則
可以這樣理解,有兩個佇列—— 執行佇列(同步任務synchronous)、事件佇列(非同步任務asynchronous),存放所有著JS執行的任務(ajax、點選事件、UI渲染等、promise.then)。
- 執行佇列存放所有同步程式碼的任務
- 事件佇列存放所有非同步程式碼的巨集任務
- 微任務處於兩個佇列之間
執行Js程式碼,從上往下執行程式碼,同步程式碼,依次執行,遇到非同步程式碼,則根據其任務型別,新增到相應的對應佇列,巨集任務放入事件佇列,微任務放於執行佇列之後(但是會是某個事件迴圈的最後),事件佇列(任務)之前。這個處理事件過程是不斷迴圈的,只要主執行緒空了,就會去讀取"任務佇列"並且執行。
demo
<script>
console.log('script start');
setTimeout(function() {
console.log('timeout1');
}, 0);
new Promise(resolve => {
console.log('promise1');
resolve();
setTimeout(() => {
console.log('timeout2');
new Promise(resolve => {
resolve();
}).then(function() {
console.log('then3')
})
}, 0);
}).then(function() {
console.log('then1')
})
new Promise(resolve => {
resolve();
}).then(function() {
console.log('then2')
})
console.log('script end');
</script>
複製程式碼
總結任務
- 任務(tasks)按循序執行,瀏覽器或許在一邊執行任務(tasks)一邊更新渲染
- 微任務(microtasks)也是按順序執行
- 在每一次回撥之後執行,只要沒有其他js正在執行
- 在每一次任務之後
- 當前執行棧執行完畢時會立刻先處理所有微任務佇列中的事件,然後再去巨集任務佇列中取出一個事件。