setTimeout(function(){console.log('delay 0 sec')}, 0)
console.log('Hello!')
執行以上程式碼,然後看到了這個結果:
為什麼「延遲0秒」的函式寫在上方,但在console印出的結果,它還是被排在第二順位?
利用AC教材提供的youtube演講連結一窺究竟:
演講提供了更多JS的細節概念,身為JS新手的我還在消化,但若針對教案提出的問題來回答,加上利用google大神查到MDN的文件,大致可以回答。
JavaScript 的並行模型(concurrency model)是基於「事件循環(event loop)」。
視覺化呈現(Visual representation)
堆疊(Stack)
呼叫函式(Function)會形成一個 frame 的堆疊。
function foo(b) {
var a = 10;
return a + b + 11;
}
function bar(x) {
var y = 3;
return foo(x * y);
}
console.log(bar(7));
當呼叫 bar 時,會產生一個含有 bar 的參數及區域變數的 frame,而在 bar 呼叫了 foo 時,含有 foo 參數及變數的第二個 frame 就會被置於堆疊的最上面。當 foo 回傳後,最上面的 frame 會被抽離堆疊(僅留下 bar 的呼叫 frame)。然後當 bar 返回之後,堆疊就會清空。
堆積(Heap)
物件被分配在一個堆積中,一個只表示記憶體中的一個無結構的大區域。
佇列(Queue)
JavaScript 執行環境包含一個訊息佇列,裡面是待處理的訊息,其中每個訊息都與一個 function 相關聯。當堆疊中有足夠空間時,會從訊息佇列拿取一個訊息進行處理,處理過程包含了呼叫相關聯的 function。只有當堆疊清空時,該訊息才算是完成處理。
事件循環(Event Loop)
第一行console程式碼被執行,排到stack上,很快的被印出。
第二行的setTimeout函式並不在V8引擎中運作,在瀏覽器之下必須類似由外頭呼叫的方式進行,其他像DOM、AJAX也都為此方式進行。呼叫後不會直接被推到stack區,會先在webapis區等待設定的延遲秒數,
此時第三行的console也會被推到stack區,跟著馬上被印出。此時setTimeout函式還在等待時間。
時間到達設定秒數後,setTimeout函式內要執行的事情會被放到task queue區,此queue必須要等到stack區完全被清空,才會往stack區堆放執行。
此狀況即使把延遲秒數設定為0依然成立,原因為:
「零延遲」並非意味著回呼函式(callback function)會在 0 毫秒之後立刻執行。當使用延遲 0 毫秒參數來呼叫 setTimeout (en-US) 函式並非是程式會過了該段時間就會執行,而是會參考佇列中等待的訊息數量。 在下面範例中,「this is just a message」會寫在 setTimeout 的回呼訊息被執行之前,因為該時間段參數是要求執行環境處理所需的最少等待時間,而非一個保證時間。
由於console.log走的路徑和setTimeout不同,造成即使先寫出setTimeout程式碼,印出的順序依然會以單純直接console.log為先,setTimeout為後,無論延遲秒數的設定為何。