我們知道JavaScript的一大特點就是單線程,而這個線程中擁有唯一的一個事件循環。JavaScript代碼的執行過程中,除了依靠函數調用棧來搞定函數的執行順序外,還依靠任務隊列(task queue)來搞定另外一些代碼的執行。
隊列數據結構
- 一個線程中,事件循環是唯一的,但是任務隊列可以擁有多個。
- 任務隊列又分爲macro-task(宏任務)與micro-task(微任務),在最新標準中,它們被分別稱爲task與jobs。
- macro-task大概包括:script(整體代碼), setTimeout, setInterval, setImmediate, I/O, UI rendering。
- micro-task大概包括: process.nextTick, Promise, Object.observe(已廢棄), MutationObserver(html5新特性)
- setTimeout/Promise等我們稱之爲任務源。而進入任務隊列的是他們指定的具體執行任務。
- 宏任務隊列的執行順序:script(整體代碼)->setTimeout(setInterval同源)->setImmediate
微任務隊列的執行順序:process.nextTick->Promise(then)
setTimeout任務的執行,也依然是藉助函數調用棧來完成,並且遇到任務分發器的時候也會將任務分發到對應的隊列中去。
只有當setTimeout中所有的任務執行完畢之後,纔會再次開始執行微任務隊列。並且清空所有的可執行微任務。
setTiemout隊列產生的微任務執行完畢之後,循環則回過頭來開始執行setImmediate隊列。仍然是先將setImmediate隊列中的任務執行完畢,再執行所產生的微任務。
當setImmediate隊列執行產生的微任務全部執行之後,第二輪循環也就結束了。
大家需要注意這裏的循環結束的時間節點。
當我們在執行setTimeout任務中遇到setTimeout時,它仍然會將對應的任務分發到setTimeout隊列中去,但是該任務就得等到下一輪事件循環執行了。例子中沒有涉及到這麼複雜的嵌套,大家可以動手添加或者修改他們的位置來感受一下循環的變化。
Example1:
console.log('golb1');
setTimeout(function() {
console.log('timeout1');
process.nextTick(function() {
console.log('timeout1_nextTick');
})
new Promise(function(resolve) {
console.log('timeout1_promise');
resolve();
}).then(function() {
console.log('timeout1_then')
})
})
setImmediate(function() {
console.log('immediate1');
process.nextTick(function() {
console.log('immediate1_nextTick');
})
new Promise(function(resolve) {
console.log('immediate1_promise');
resolve();
}).then(function() {
console.log('immediate1_then')
})
})
process.nextTick(function() {
console.log('glob1_nextTick');
})
new Promise(function(resolve) {
console.log('glob1_promise');
resolve();
}).then(function() {
console.log('glob1_then')
})
setTimeout(function() {
console.log('timeout2');
process.nextTick(function() {
console.log('timeout2_nextTick');
})
new Promise(function(resolve) {
console.log('timeout2_promise');
resolve();
}).then(function() {
console.log('timeout2_then')
})
})
process.nextTick(function() {
console.log('glob2_nextTick');
})
new Promise(function(resolve) {
console.log('glob2_promise');
resolve();
}).then(function() {
console.log('glob2_then')
})
setImmediate(function() {
console.log('immediate2');
process.nextTick(function() {
console.log('immediate2_nextTick');
})
new Promise(function(resolve) {
console.log('immediate2_promise');
resolve();
}).then(function() {
console.log('immediate2_then')
})
})
Example2:
setTimeout(function() {
console.log(1);
setTimeout(function() {
console.log(2);
}, 0);
process.nextTick(function() {
console.log(5);
process.nextTick(function() {
console.log(6);
});
});
}, 0);
process.nextTick(function() {
console.log(3);
process.nextTick(function() {
console.log(4);
});
setTimeout(function() {
console.log(7);
}, 0);
});
結果:3 4 1 7 5 6 2