我們今天來說說javaScript中的代碼執行順序問題,這是一道非常經典的面試題。
這里我們需要知道的一個知識點是:javascript是一門單線程的腳本語言,代碼的執行順序是自上而下執行的,我們來看一下下面這段代碼的執行結果:
console.log(1);
console.log(2);
console.log(3);
//執行結果:1;2;3;
這段代碼是自上而下執行的。
我們再看下面這段代碼的執行結果:
console.log(1);
setTimeout(function () {
console.log(2);
}, 0);
console.log(3);
// 執行結果:1;3;2;
這段代碼的setTimeout里面的代碼是在最后才執行的。這是因為setTimeout() 方法用於在指定的毫秒數后調用函數或計算表達式(W3CSchool對SetTImeout的定義)。按照定義來看,執行結果也應該是1;2;3; ,然而實際結果卻是1;3;2;。這是為什么呢?這就要從瀏覽器對代碼的執行機制來說起了。
那么,來看看下圖:

(上圖轉自Philip Roberts的演講《Help, I'm stuck in an event-loop》)
我們來看幾個摘自MDN的專業術語
heap(堆):對象被分配在堆中,堆是一個用來表示一大塊(通常是非結構化的)內存區域的計算機術語。
stack(棧):函數調用形成了一個由若干幀組成的棧。
WebAPIS:囊括 Web 強大腳本能力的每個 API 參考資料, 包括 DOM 、所有相關的 APIs 及可以用來構建 Web 的相關接口。
隊列(event queue):一個 JavaScript 運行時包含了一個待處理消息的消息隊列。每一個消息都關聯着一個用以處理這個消息的回調函數。
—— 摘自MDN
因為javascript是單線程的腳本語言,代碼自上而下執行,代碼在執行時會被壓入執行棧(stack)中,當遇到setTimeout時會將setTimeout函數交給Web API來維護,當異步任務(比如:setTimeout)執行完成后會將對應的回調函數推入事件隊列(event queue)中,當執行棧中任務全部執行完成之后瀏覽器會讀取任務隊列,把對應的回調函數再壓入執行棧中,然后循環執行。這就是所謂的EventLoop。我們再來看看下圖,執行結果會是怎樣的呢?

執行結果:1;2;3;7;5;4;6;
為什么會得到這樣的執行結果呢?為什么不是1;2;3;7;4;5;6;呢?這里涉及到了Macrotask 和 Microtask,即宏任務隊列和微任務隊列。setTimeout屬於Macrotask,而Promise屬於Microtask,從上圖執行結果可以看出Microtask優先級高於Macrotask,當執行棧全部為空時,先詢問是否有微任務,如果有,先執行為任務,全部執行完成后再執行宏任務。
我們再來看看下面這段代碼的執行結果:

執行結果:1;2;3;8;5;6;4;7;
與Macrotask有所不同的是,Microtask中的任務不會一個一個壓入執行棧中,而是直接壓入執行棧,從上圖執行結果可以得到佐證。Promise即使放入另外一個Promise的回調函數里,也會先執行Promise的回調,再執行setTimeout的回調。
【免責聲明:本文圖片及文字信息均由千鋒重慶web前端培訓小編轉載自網絡,旨在分享提供閱讀,版權歸原作者所有,如有侵權請聯系我們進行刪除。】