本文原創:liuwanhtml
在說微任務與宏任務以前咱們先說一下同步任務與異步任務的概念吧。bash
JavaScript語言的一大特色就是單線程,也就是說,同一個時間只能作一件事。單線程就意味着,全部任務須要排隊,前一個任務結束,纔會執行後一個任務。若是前一個任務耗時很長,後一個任務就不得不一直等着。網絡
若是排隊是由於計算量大,CPU忙不過來,倒也算了,可是不少時候CPU是閒着的,由於IO設備(輸入輸出設備)很慢(好比Ajax操做從網絡讀取數據),不得不等着結果出來,再往下執行。異步
JavaScript語言的設計者意識到,這時主線程徹底能夠無論IO設備,掛起處於等待中的任務,先運行排在後面的任務。等到IO設備返回告終果,再回過頭,把掛起的任務繼續執行下去。async
因而,全部任務能夠分紅兩種,一種是同步任務(synchronous),另外一種是異步任務(asynchronous)。同步任務指的是,在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務;異步任務指的是,不進入主線程、而進入"任務隊列"(task queue)的任務,只有"任務隊列"通知主線程,某個異步任務能夠執行了,該任務纔會進入主線程執行。 具體來講,異步執行的運行機制以下。(同步執行也是如此,由於它能夠被視爲沒有異步任務的異步執行。)函數
以上摘自廖雪峯的博客 JavaScript 運行機制詳解:再談Event Loop.oop
咱們先看一下下面的代碼,而後思考一下輸出的前後順序post
setTimeout(() =>{
console.log('1')
});
new Promise((resolve) => {
console.log('2');
resolve();
}).then(() => {
console.log('3')
});
console.log('4');
複製代碼
按照同步與異步的概念來看,輸出順序應該是二、四、一、3. 可是,打開控制檯,輸入代碼,查看輸出,順序是這樣的二、四、三、1,發生了什麼? ui
想知道發生了什麼就繼續往下看吧。除了廣義的同步任務和異步任務,咱們對任務有更精細的定義,分爲宏任務和微任務。spa
注:Promise當即執行,then函數分發到「microtask」隊列,process.nextTick分發到「microtask」隊列 js引擎會把宏任務和微任務放置兩個「任務隊列」中,分別是「macrotask」隊列以及「microtask」隊列。在執行異步任務時,先執行宏任務,而後在執行微任務。
因此如今解釋一下上面問題中的輸出順序問題:
因此上述問題的輸出順序知道怎麼肥死了吧? 盜一張事件循環,宏任務,微任務的關係圖,以下:
圖說明:進入總體代碼(宏任務)後,開始第一次循環。接着執行全部的微任務。而後再次從宏任務開始,找到符合執行條件的一個宏任務執行完畢,再執行全部的微任務。複習一下上面的知識點,咱們瞅一眼如下代碼:
console.log('1');
setTimeout(() => {
console.log('2');
process.nextTick(() => {
console.log('3');
})
setTimeout(() => {
console.log('10')
new Promise((resolve) => {
console.log('11');
resolve();
}).then(() => {
console.log('12')
})
})
new Promise((resolve) => {
console.log('4');
resolve();
}).then(() => {
console.log('5')
})
})
process.nextTick(() => {
console.log('6');
})
new Promise((resolve) => {
console.log('7');
resolve();
}).then(() => {
console.log('8')
setTimeout(() => {
console.log('9')
})
})
console.log('10')
複製代碼
大聲說出答案吧:一、七、十、六、八、二、四、三、五、九、十、十一、12 好吧,咱們分析一下:
fn1
,放置「macrotask」隊列中,接着執行下面的代碼fn2
放置「microtask」隊列fn3
會被放置「microtask」隊列fn2
以及fn3
),隊列具體先進先出的特色,因此先執行fn2
,輸出6,而後執行fn3
,輸出8,裏面包含setTimeout,把它的回調函數fn4
放置「macrotask」隊列中。fn1
、fn4
fn1
。fn1
,遇到console.log('2'),就輸出,遇到process.nextTick,將其回調函數fn5
放置「microtask」隊列,遇到setTimeout,把它的回掉函數fn6
放置「macrotask」隊列中,遇到Promise,new Promise會當即執行,因而輸出4,其then函數fn7
會被放置「microtask」隊列,即這個宏任務執行完成。「macrotask」隊列裏面有fn4
、fn6
。fn5
、fn7
,把裏面的任務所有執行完畢, 先執行fn5
,輸出3,再執行fn7
,輸出5fn4
、fn6
,執行fn4
,輸出9.fn6
fn6
,輸出10,遇到new Promise,輸出11,並把其回調函數fn8
放置「microtask」隊列,至此宏任務fn6
結束,fn8
,輸出12,至此,「macrotask」隊列以及「microtask」隊列所有空了。