首先你們都知道javascript是單線程語言。javascript
什麼是單線程呢?好比咱們去車站買票,只有一個售票窗口,你們排隊買票,須要前面的人買完票,後面的人才能買票。html
那爲何javascript不能是多線程呢?java
這主要和它的用途有關。假如javascript能夠多線程,例如操做DOM元素,一個線程往DOM裏面添加內容,另外一個線程則刪除內容,那這時瀏覽器應該用哪一個線程呢?瀏覽器
任務隊列多線程
從上面的例子中能夠知道,單線程是有一個隊列的,而且是有順序的執行(按順序買票)。異步
咱們在考慮一個問題。假如第一個買票的人遇到一些問題,那後面的人不是得一直等着?函數
回到javascript中,咱們優秀的設計者固然能想到這個問題。因而就出現了「同步任務」和「異步任務」,簡單點來講,同步是阻塞模式,而異步是非阻塞模式。oop
javascript代碼執行時,會有一個主線程,主線程會將同步任務一個一個往下執行,spa
當遇到異步任務時,會將異步任務放入任務隊列。線程
當主線程把代碼執行完以後,在從任務隊列中取出一個任務放入主線程中執行。
console.log("1"); setTimeout(()=>{ console.log("2"); }) console.log("3");
咱們看一下上面這段代碼的執行順序:
第一步:首先會輸出 1
第二步:當執行到setTimeout時,它會被放到任務隊列中,由於它是異步的。
第三步:輸出 3
第四步:主線程已執行完畢,主線程將setTimeout從任務隊列中取出執行,輸出 3
宏任務與微任務
任務隊列又分宏任務和微任務。
宏任務:總體代碼script,setTimeout,setInterval等。
微任務:Promise,process.nextTick等。
即然有分類,那它倆確定是有區別的,微任務優先於宏任務執行。
setTimeout(()=>{ console.log("定時器執行了"); }) new Promise((resolve)=>{ for(var i = 0;i<100;i++){ i==20&&resolve() } }).then(()=>{ console.log("then函數執行了"); }) console.log("程序結束");
瀏覽器輸出結果以下
setTimeout和then都屬於異步,按理說應該是setTimeout先執行的呀,但爲何then先執行了?
其實上面已經解釋過了,由於Promise屬於微任務,而setTimeout屬於宏任務,因此then先執行。
若是還不理解,咱們看下面這個例子。
setTimeout(()=>{//定時器1 new Promise((resolve)=>{ console.log("1"); resolve(); }).then(()=>{//then console.log("2"); }) setTimeout(()=>{//定時器1的子定時器 console.log("3"); }) }) setTimeout(()=>{//定時器2 console.log("4"); })
咱們來分析一下上面的代碼:
第一步:代碼中有兩個定時器,分別是「定時器1」和「定時器2」,由於它倆都屬於異步任務,因此會被加入到任務隊列中。
第二步:除了「定時器1」和「定時器2」(這個兩個任務都屬於宏任務),已經沒有別的任務了,此時主程序將「定時器1」從任務隊列中取出來執行。而後輸出1 ,
「定時器1」裏面還有有兩個任務,分別是微任務"then" 和 宏任務「定時器1的子定時器」,它倆都會被添加到任務隊列中,這時任務隊列中有「定時器2」、「then」、「定時器1的子定時器」。
第三步:主程序將「then」從任務隊列中取出並執行,輸出2 ,爲何會先取"then"呢?按代碼順序不該該是「定時器2」、「then」、「定時器1的子定時器」 ?由於"then"是微任務,因此它的優先級
高於「定時器2」、「定時器1的子定時器」,也能夠理解爲,當有微任務進入隊列時,它會放到宏任務的前面。
第四步:而後主程序會按順序將「定時器2」、「定時器1的子定時器」從任務隊列中取出來執行。
因此最後打印的結果是:1 2 4 3
你們有沒有發現,主程序執行完成以後,它會從任務隊列中去讀取一個任務,而後放到主程序中執行。
只要任務隊列中還有任務,主程序就會一直進行「讀取」、「執行」操做。
因此能夠得出如下結論:主線程從"任務隊列"中讀取事件,這個過程是循環不斷的,因此整個的這種運行機制又稱爲Event Loop(事件循環)。