EventLoop
以下內容僅限于自己理解,可能并不全面或者有錯誤
參考文檔:https://www.cnblogs.com/jymz/p/7900439.html
JavaScript的一大特點就是單線程,而這個線程中擁有唯一的一個事件循環
依靠函數調用棧來搞定函數的執行順序外,還依靠任務隊列(task queue)來搞定另外一些代碼的執行。
函數調用棧遵循的是先進后出的原則;
1? 一個線程中,事件循環是唯一的,但是任務隊列可以擁有多個。
2 任務隊列又分為macro-task(宏任務)與micro-task(微任務),在最新標準中,它們被分別稱為task與jobs。
3 macro-task大概包括:script(整體代碼), setTimeout, setInterval, setImmediate, I/O, XHR回調,事件回調(鼠標鍵盤事件)。
4 micro-task大概包括:process.nextTick, Promise.then(), MutationObserver(html5新特性)
5 setTimeout/Promise等我們稱之為任務源。而進入任務隊列的是他們指定的具體執行任務。
6 來自不同任務源的任務會進入到不同的任務隊列。其中setTimeout與setInterval是同源的。
7 事件循環的順序,決定了JavaScript代碼的執行順序。它從script(整體代碼)開始第一次循環。之后全局上下文進入函數調用棧。直到調用棧清空(只剩全局),然后執行所有的micro-task。當所有可執行的micro-task執行完畢之后。循環再次從macro-task開始,找到其中一個任務隊列執行完畢,然后再執行所有的micro-task,這樣一直循環下去。
8 其中每一個任務的執行,無論是macro-task還是micro-task,都是借助函數調用棧來完成。
看下面的案例
console.log('start')
setTimeout(function() {?
????console.log(timeout1)
?},0)
new Promise(function (resolve) {
? ?console.log('promise1')
? ?for (var i = 0; i <1000; i++) {
? ? ? ? i == 99 && resolve()
? ?}
}).then(function() {
? ?console.log('then1')
?})
?console.log('global')
具體的執行過程:
首先,事件循環從宏任務開始,從script開始,不同的任務源的任務進入到不同的事件隊列;
宏任務timeout1進入setTimeout隊列
script執行時遇到Promise實例。Promise構造函數中的第一個參數,是在new的時候執行,因此不會進入任何其他的隊列,而是直接在當前任務直接執行了,
如下圖
當promise 中的resolve執行完畢,出調用棧后,后續的.then會被分發到micro-task的Promise隊列中去;具體如下圖
script任務繼續往下執行,最后只有一句輸出了globa1,然后,全局任務就執行完畢了。
第一個宏任務script 執行完畢后,則開始執行微任務隊列中的任務;then1被輸出;則第一輪事件循環執行完畢。如下圖,script隊列為空,微任務隊列為空
開始執行第二輪事件循環,第二輪也是從宏任務開始,執行settimeout隊列中的任務;
需要注意的是如果settimeou任務中遇到settimeout,任務也會放到settimeout隊列,但是要到下一輪事件循環中;
setTimeout(function() {
??? ????console.log('timeout_1');
??? ????setTimeout(function() {
????? ????????console.log('timeout_2');
??? ????},0)
? },0);
? setTimeout(function() {
??? ????console.log('timeout_3');
? },0) // 執行結果為 timeout_1?timeout_2 timeout_3
每一輪事件循環會先執行宏任務,宏任務執行完了立刻執行所產生的微任務;比如在settimeout中有promise,則在settimeout中的同步代碼被執行完之后立即執行.then()中的代碼;
3/不同的任務源的執行順序
宏任務:
settimeout隊列 要比setImmediate隊列先執行;
如果settimeout和setImmediate兩者都有,則執行完settimeout隊列任務以及其所產生的微任務隊列,清空所有微任務隊列,之后再執行setImmediate任務隊列及其產生的的微任務隊列。
微任務:
process.nextTick()要比promise的.then()先執行;
如果一個宏任務產生了process.nextTick()和promise的.then()兩個微任務隊列,則process.nextTick()隊列任務執行清空之后,再執行promise的.then()
XHR回調會在數據響應回來插入到事件隊列中,而事件回調則會在被觸發時插入到事件隊列中;