先來段代碼javascript
const p = new Promise((resolve, reject) => {
console.log(1)
})
console.log(2);
setTimeout(function () {
console.log(3);
}, 1000);
p.then((data) => {
console.log(4)
})
console.log(5);
複製代碼
讓咱們來看看打印結果的順序java
1
2
5
4
3
複製代碼
由於javascript是單線程的,只有一個主線程。主線程會先執行同步代碼,異步操做會被放入一個任務隊列中。等到同步代碼執行完成以後,再執行異步任務。 常見的異步操做有:node
異步任務又分爲宏任務
和微任務
。 常見的宏任務有:setTimeout
、setInterval
、setImmediate
、MessageChannel
常見的微任務有:Promise的then方法
、process.nextTick
、MutationObserver
瀏覽器
總結javascript執行順序以下: 1
,如果同步任務,則 主線程中執行;若是是異步任務,就放到一個任務隊列裏 2
,開始執行主線程中的同步任務,直到將主線程中的全部任務都走完,此時同步任務清空了 3
,回過頭看異步隊列裏若是有異步任務完成了,就生成一個事件並註冊回調,放入主線程中 4
,再返回第3步,直到異步隊列都清空,程序運行結束異步
這就是所謂的事件環機制,瀏覽器和node中的事件環機制有所不一樣。ui
在瀏覽器的執行環境中,老是先執行微任務,再執行宏任務,再來看看第一段代碼,爲何Promise的then方法在setTimeout以前執行?其根本原理就是由於Promise的then方法是一個微任務,而setTimeout是一個宏任務。 執行順序爲: 1
,先執行微任務,清空微任務隊列 2
,再執行宏任務,如宏任務中有微任務,則繼續執行微任務,直至清空微任務隊列 3
,循環上述操做,直至全部任務完成spa
圖中每個階段都表明了一個宏任務隊列,在Node事件環中,優先執行微任務
,微任務的運行時機是在每個「宏任務隊列」清空以後,在進入下一個宏任務隊列之間執行。這是和瀏覽器的最大區別。線程
與瀏覽器事件環有所不一樣的是:瀏覽器事件環是遇到微任務會清空整個微任務隊列。 Node事件環是清空完一個階段的宏任務隊列以後再清空微任務隊列。
code
來看一段代碼:cdn
const fs = require('fs');
fs.readFile('./1.txt', (err, data) => {
setTimeout(() => {
console.log('timeout');
});
setImmediate(() => {
console.log('immediate');
});
Promise.resolve().then(() => {
console.log('Promise');
});
});
複製代碼
執行結果爲:
Promise
immediate
timeout
複製代碼
代碼並不複雜,首先使用fs模塊讀取了一個文件,在回調的內部有兩個宏任務和一個微任務,微任務老是優於宏任務執行的,所以先輸出Promise。 可是以後的區別爲何先輸出immdiate?緣由就在於fs讀取文件的宏任務在上圖中的第4個輪詢階段,當第4個階段清空隊列以後,就該進入第5個check階段,也就是setImmediate這個宏任務所在的階段,而不會跳回第1個階段,所以先輸出immedate。