事件循環機制控制了javascript代碼的執行順序。咱們都知道javascript是單線程,這個線程中擁有惟一的一個事件循環。(新標準web workker有多線程的概念。)而事件循環機制主要以來調用棧來處理執行順序,依靠任務隊列來執行代碼的執行。隊列的概念能夠參考https://segmentfault.com/a/11...
在一個線程中,調用棧是惟一的,可是任務隊列能夠是多個,而且分爲macro-task(宏任務)及micro-task(微任務)兩種類型。
這裏須要區分一個概念任務及任務源。setTimeout及Promise是任務源。他們指定具體的執行任務進入任務隊列。只有回調中的函數纔會進入任務隊列。就像setTimeout它實際上是麗姬執行的,只是它的回調函數纔會延遲執行。promise也是,自己是當即執行的,可是then纔會在「將來」執行。
javascript的執行順序是從總體代碼開始作循環,以後全局上下文進入函數調用棧。直到調用棧清空。總體代碼所處的macro-task執行完成,輪到micro-task任務執行。一直循環直到全部的任務執行完成。
固然,不一樣的任務源的任務會進入不一樣的任務隊列。
具體的能夠參考一下代碼。
1.事件循環從macro-task開始,總體代碼開始執行。總體代碼script進入macro-task,而且執行代碼main進入調用函數調用棧。遇到第12行的打印輸出start
2.繼續執行,遇到13行的setTimeout,它是宏任務源。便將其分發到對應的隊列中。接着遇到16行的promise。promise.resolve會進入函數調用棧直接執行,所以打印promise1,接着將p.then1和p.then分發到對應的微任務隊列中。繼續執行代碼,遇到第24行的打印便輸出end。大體圖示以下圖。
3.script執行完畢,即第一個宏任務執行完畢,開始執行微任務。如今微任務只有一個隊列,裏面有p1.then1,p1.then2。隊列是先進先出,所以先執行p1.then1,p1.then1進入函數調用棧,輸出then1。
4. p1.then1執行完畢以後,出棧。可是此時的正在進行的微任務還未執行完完畢,會繼續執行p1.then2,p1.then2進入函數調用棧,輸出then2。此時,微任務正在進行的隊列已經執行完畢。
5.當微任務執行完畢以後,第一輪循環結束,進入第二輪循環,繼續執行宏任務,此時setTimeout執行,進入函數調用棧,輸出setTimeout1。
6.此時,宏任務隊列和微任務隊列中都沒有任務了。代碼執行完畢,就不會有任何輸出了。javascript
咱們上述的代碼只涉及到一個宏任務及微任務隊列的狀況。但若是狀況更加複雜會有什麼樣的表現呢?你們能夠看看下面的代碼。根據上面的原理試着本身分析下結果~
1.仍是跟之前的例子同樣,事件循環從macro-task開始,總體代碼開始執行。輸出start。setTimeout1,setTimeout2依次進入新的宏任務隊列。p3.resolve執行,輸出promise31,promise31。並將setTimeout3放入新的宏任務隊列。由於setTimeout3不是總體代碼中定義的,而是在promise中定義的,須要從新開啓一個宏任務隊列。而後p3.then1,p3.then2分別進入微任務隊列。p3.resolve出棧後,總體代碼繼續執行,這裏就不從新畫圖了,輸出end。
2.總體代碼已執行完成,循環進入微任務。此時p3.then1進入函數調用棧。輸出then31。遇到新的定時,將set4放入宏任務隊列。遇到新的promise,繼續將p4.resolve入棧。輸出promise41,promise42。遇到新的定時,將set5放入宏任務隊列。此時須要注意的是,在微任務中繼續有promise。此時的promise.then再也不進入微任務隊列,而是直接執行。所以輸出then41。
3.微任務隊列還未執行完畢,繼續執行p3.then2。直接輸出then32。此時微任務隊列已經執行完畢,進入下一輪循環。
4.新的循環開始。隊列是先進先出,所以在宏任務當前隊列中,set1先執行,進入函數調用棧。輸出setTimeout1。遇到新的promise,繼續將p1.resolve入棧。輸出promise1。仍是跟上看同樣,在宏任務中繼續有promise。此時的promise.then再也不進入微任務隊列,而是直接執行。直接輸出then1。
5.setTimeout1執行完畢,正在執行的宏任務隊列還有任務,繼續執行setTimeout2。setTimeout2進入函數調用棧。跟setTimeout1的分析同樣,陸續輸出setTimeout2,promise2,then2。
6.當前宏任務執行完畢,微任務內沒有可執行的隊列。繼續下一輪循環。執行set3。輸出setTimeout3。遇到新的promsie,仍是跟上面的分析同樣,輸出promise5,then5。由於微任務一直沒有可執行的隊列。宏任務內的隊列依次執行,輸出setTimeout4,setTimeout5。java