摘要:而事件循環機制主要以來調用棧來處理執行順序,依靠任務隊列來執行代碼的執行。當微任務執行完畢之后,第一輪循環結束,進入第二輪循環,繼續執行宏任務,此時執行,進入函數調用棧,輸出。
? ? ??事件循環機制控制了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.此時,宏任務隊列和微任務隊列中都沒有任務了。代碼執行完畢,就不會有任何輸出了。
? ? ? 我們上述的代碼只涉及到一個宏任務及微任務隊列的情況。但如果情況更加復雜會有什么樣的表現呢?大家可以看看下面的代碼。根據上面的原理試著自己分析下結果~
? ? ? 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。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/104733.html
摘要:主線程不斷重復上面的三步,此過程也就是常說的事件循環。所以主線程代碼執行時間過長,會阻塞事件循環的執行。參考資料這一次,徹底弄懂執行機制任務隊列的順序機制事件循環搞懂異步事件輪詢與中的事件循環 1. 說明 讀過本文章后,您能知道: JavaScript代碼在瀏覽器中的執行機制和事件循環 面試中經常遇到的代碼輸出順序問題 首先通過一段代碼來驗證你是否了解代碼輸出順序,如果你不知道輸出...
摘要:了解事件循環機制有助于理解的執行過程,同時這也是面試常見題。那么這個回調函數將在何時由誰執行呢已知是瀏覽器環境提供的,因此瀏覽器將對它進行處理,瀏覽器會在本次事件完成,即計時結束后,將回調函數加入循環隊列中,然后等待被加入執行棧執行。 如果有人問JavaScript是什么,也許你會說它是一個單線程、非阻塞、異步、解釋型的腳本語言。那么作為一個單線程語言,它是怎么實現非阻塞、異步的?這就...
摘要:如果沒有其他異步任務要處理比如到期的定時器,會一直停留在這個階段,等待請求返回結果。執行的執行事件關閉請求的,例如事件循環的每一次循環都需要依次經過上述的階段。因此,才會早于執行。 showImg(https://segmentfault.com/img/bVbnY76); 概念 同步任務(Synchronous) 在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行后一個任務 ...
摘要:主線程要明確的一點是,主線程跟執行棧是不同概念,主線程規定現在執行執行棧中的哪個事件。主線程循環即主線程會不停的從執行棧中讀取事件,會執行完所有棧中的同步代碼。以上參考資料詳解中的事件循環機制中的事件循環運行機制詳解再談 showImg(https://segmentfault.com/img/remote/1460000015317437?w=1920&h=1080); 前言 大家都...
摘要:前端基礎進階正是圍繞這條線索慢慢展開,而事件循環機制,則是這條線索的最關鍵的知識點。特別是中正式加入了對象之后,對于新標準中事件循環機制的理解就變得更加重要。之后全局上下文進入函數調用棧。 showImg(https://segmentfault.com/img/remote/1460000008811705); JavaScript的學習零散而龐雜,因此很多時候我們學到了一些東西,但...
閱讀 1002·2023-04-25 19:35
閱讀 2658·2021-11-22 09:34
閱讀 3690·2021-10-09 09:44
閱讀 1723·2021-09-22 15:25
閱讀 2939·2019-08-29 14:00
閱讀 3374·2019-08-29 11:01
閱讀 2598·2019-08-26 13:26
閱讀 1740·2019-08-23 18:08