先來看看一段js代碼:node
console.log('script begin') setTimeout(() => { console.log('setTimeout') },0) new Promise((resolve) => { console.log('promise begin') for(let i = 0; i < 1000; i++) { i == 999 && resolve() } }).then(() => { console.log('promise then') }) console.log('script end')
在node命令行裏執行這段js代碼,輸出的狀況爲script begin-promise begin——script end——promise then——setTimeout,爲何會這樣呢?咱們先來了解幾個概念.promise
js單線程意思就是同一時間只能作一件事,按照前後順序執行.那麼,爲何JavaScript不能有多個線程呢?這樣能提升效率啊. 瀏覽器
JavaScript的單線程,與它的用途有關。做爲瀏覽器腳本語言,JavaScript的主要用途是與用戶互動,以及操做DOM。這決定了它只能是單線程,不然會帶來很複雜的同步問題。好比,假定JavaScript同時有兩個線程,一個線程在某個DOM節點上添加內容,另外一個線程刪除了這個節點,這時瀏覽器應該以哪一個線程爲準? 異步
因此,爲了不復雜性,從一誕生,JavaScript就是單線程。async
單線程就意味着,全部任務須要排隊。全部任務能夠分紅兩種,一種是同步任務(synchronous),另外一種是異步任務(asynchronous)。 函數
同步任務:在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務; oop
異步任務:不進入主線程、而進入"任務隊列"(task queue)的任務,只有"任務隊列"通知主線程,某個異步任務能夠執行了,該任務纔會進入主線程執行。spa
下圖是主線程和任務隊列的示意圖:命令行
瞭解完主線程,還要了解一下任務,任務有宏任務(MacroTask)和微任務(MicroTask)之分。 線程
宏任務主要有:script代碼段、setTimeout、setInterval、Promise的構造函數、setImmediate、I/O等.
微任務主要有:process.nextTick和Promise的回調這兩種狀況.
若是宏任務在本輪Event Loop中執行,則微任務在本輪Event Loop的全部宏任務結束後執行(Event Loop下面會講到)。下面爲宏任務和微任務的執行示意圖:
主線程從"任務隊列"中讀取事件,這個過程是循環不斷的,因此整個的這種運行機制又稱爲Event Loop(事件循環)
咱們再回頭看看開始的一段代碼:
首先任務進入執行棧,除了setTimeout,其餘的進入主線程執行,setTimeout則進入任務隊列; 而後主線程裏面的任務又有宏任務和微任務,先執行宏任務,微任務在全部宏任務結束後執行; 因此先輸出script begin-promise begin——script end——promise then; 最後主線程讀任務隊列的異步任務,最後輸出setTimeout