自學日記

學習程式相關知識及學習法

0%

JS同步與非同步 堆疊(stack)與佇列(Queue)

同步與非同步/堆疊(Stack)與佇列(Queue)

單執行緒與同步執行

Javascript是單執行緒和同步執行,單執行緒表示一次只能執行一個指令,同步,一次執行一個而且是照順序的。

呼叫(invocation)與執行堆(stack)

我們先來說明,在程式開始前,全域執行環境會先被創造,因為語法解析器會分析程式,然後編譯器會編譯程式進而創造全域環境,最後將這些函釋放進記憶體中
瞭解上述道理之後,我們以下列function來說明:

1
2
3
4
5
6
7
8
9
10
11
12
// 1.最先全域執行環境被創造,在執行堆中最底層(最先所以最底層)
// 5.function b執行,被放入執行堆,因為最後放入因此最先執行
function b() {
}
// 3.a()呼叫創造新執行環境給下方function a(),同時進入執行堆
function a() {
// 4.function a()中有b(),因此一樣,b()創造新的執行環境給function b(),並進入執行堆
b();
}

a();//2.其次a()呼叫

上面例子中,
function b&a都在記憶體中然後程式碼逐行執行,但是,還不會執行函數中的程式,因為未被呼叫
下圖為,全域環境被創造出來,但未被呼叫,functio a、b未執行:

再來,程式範例中,最後a()進行呼叫。因此新的執行環境被創造,那麼他就會如下圖,進入執行推(steck)中,像是新東西被堆上去,越上面的越先被執行 :

再來function a()中有b(),因此b 函式的呼叫,新的執行環境成立給b函式,一樣進入執行堆中,如下圖:

所以當函數呼叫,一個新的執行環境就被創造(創造階段)給函數,而新的執行環境會進入執行堆,最後當執行完畢後,離開執行堆,最上面的即正在執行的程式,逐行並同步的,如下圖,function b()執行完之後離開執,繼續執行function a(),以此類推,直到逐行執行完畢。

以此我們可以上方範例去進行驗證:

可以確定在執行堆的作用下,function b最先印出,其次是function a

非同步事件(asynchronous callback)

如果JS程式是逐行執行,那為什麼它可以監控瀏覽器的一些事件(event)呢?像是偵測滑鼠的點擊、滑動等等這類的非同步呼叫(asynchronous callback)。
首先,JS引擎只是在瀏覽頁(Browser)中的其中一個部分,另外還包含其它的部分,像是rendering engine和http request,也就是說,整個網頁在執行的過程中可以是非同步(asynchronous)的,但就JS引擎本身,確實是同步的。

事件佇列(event Queue)

因此,這牽扯到事件佇列的部分,在JS引擎內的等待列稱為事件佇列Event Queue,如果有事件發生,如瀏覽器頁面的滑鼠點擊事件,JS引擎會先將事件放在事件佇列Event Queue,當執行環境的程式都執行完成後,JS才會開始注意事件佇列,此時檢查是否有函式被事件觸發,如果事件觸發函式,就創造執行環境給對應函式,所以說,當瀏覽器的其他引擎或處理器有事件、狀態發生並與JS引擎互動時,這些事件就會被註冊進事件佇列裡。

以下以JS奇怪的部分內的範例舉例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function waitThreeSeconds(){
var ms = 3000 + new Date().getTime();
while(new Date() < ms){}
console.log("finished function");
}

function clickHandler(){
console.log("click event!");
}

document.addEventListener('click', clickHandler);
console.log("started execution");
waitThreeSeconds();
console.log("finished execution");

結果印出為:

因此此處重複做小總結,JS會在處理完執行堆之後,檢查Event Queue是否有事件被註冊,再做處理。

參考資料: