事件循環機制

我們知道JavaScript的一大特點就是單線程,而這個線程中擁有唯一的一個事件循環。JavaScript代碼的執行過程中,除了依靠函數調用棧來搞定函數的執行順序外,還依靠任務隊列(task queue)來搞定另外一些代碼的執行。

這裏寫圖片描述

隊列數據結構

  1. 一個線程中,事件循環是唯一的,但是任務隊列可以擁有多個。
  2. 任務隊列又分爲macro-task(宏任務)與micro-task(微任務),在最新標準中,它們被分別稱爲task與jobs。
  3. macro-task大概包括:script(整體代碼), setTimeout, setInterval, setImmediate, I/O, UI rendering。
  4. micro-task大概包括: process.nextTick, Promise, Object.observe(已廢棄), MutationObserver(html5新特性)
  5. setTimeout/Promise等我們稱之爲任務源。而進入任務隊列的是他們指定的具體執行任務。
  6. 宏任務隊列的執行順序: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

參考文章:http://www.jianshu.com/p/12b9f73c5a4f

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章