Event Loop 事件迴圈
阿新 • • 發佈:2019-02-19
同步和非同步
首先要明確:
JS是單執行緒語言
也就是說,JS一次只能做一件事情。
cpu處理指令速度非常快,遠比磁碟I/O和網路I/O速度快,所以一些cpu直接執行的任務就成了優先執行主線任務(即同步任務synchronous),然後需要io返回資料的任務就成了等待被執行的任務(即非同步任務asynchronous)
- 同步任務:在主執行緒上排隊執行的任務,前一個任務執行完畢,才能執行後一個任務;
- 非同步任務:不進入主執行緒、而進入”任務佇列”(task queue)的任務,只有”任務佇列”通知主執行緒,某個非同步任務可以執行了,該任務才會進入主執行緒執行。
所以:
當要主執行緒任務完成會後,就會去讀取”任務佇列”,這就是JavaScript的執行機制
Microtasks 和 Macrotasks
具體到任務佇列,又分位 microtasks 和 macrotasks
屬於microtasks的任務有:
- process.nextTick
- promise
- Object.observe
- MutationObserver
屬於macrotasks的任務有:
- setTimeout
- setInterval
- setImmediate
- I/O
- UI渲染
在執行事件時:
- 從script(整體程式碼)開始第一次迴圈。執行所有主執行緒的同步函式,遇到非同步函式,分別新增到microtasks和macrotasks任務佇列
- 同步函式執行後,開始執行非同步函式中的任務佇列,首先執行所有的micro-task
- 所有的micro-task執行完成後,迴圈執行macro-task任務
- macro-task任務執行,再次執行micro-task,這樣一直迴圈下去
簡單來件,整體的js程式碼在執行完主執行緒的同步任務,然後有microtask執行microtask,沒有microtask執行下一個macrotask,如此往復迴圈至結束
練習
setTimeout(function() {
console.log(4)
}, 0);
new Promise(function(resolve) {
console.log(1)
for (var i = 0; i < 10000; i++) {
i == 9999 && resolve()
}
console.log(2)
}).then(function() {
console.log(5)
});
console.log(3);
// 結果是1 2 3 5 4
再來一個:
console.log('start')
const interval = setInterval(() => {
console.log('setInterval')
}, 0)
setTimeout(() => {
console.log('setTimeout 1')
Promise.resolve().then(() => {
console.log('promise 3')
}).then(() => {
console.log('promise 4')
}).then(() => {
setTimeout(() => {
console.log('setTimeout 2')
Promise.resolve().then(() => {
console.log('promise 5')
}).then(() => {
console.log('promise 6')
}).then(() => {
clearInterval(interval)
})
}, 0)
})
}, 0)
Promise.resolve().then(() => {
console.log('promise 1')
}).then(() => {
console.log('promise 2')
})
// 執行順序是
// start
// promise 1
// promise 2
// setInterval
// setTimeout 1
// promise 3
// promise 4
// setInterval
// setTimeout 2
// promise 5
// promise 6