JavaScript Weird Part (12) - 關於非同步回呼

非同步的意思是在一個時間點不只一個,可能有一段程式碼在執行時會開始執行另一段程式碼,繼而再執行別的程式碼,這些程式碼在 Javascript 引擎內是同時在執行的。

雖然 Javascript 是同步的(synchronous) ,它不會非同步的執行,一次執行一行程式碼。然而像是點擊事件或是從 Javascript 取得資料,有 callback 函數可以執行,當事件或動作完成,因為 Javascript 是同步的,會怎麼處理非同步事件呢?

Javascript 在執行時,它並非獨立存在,它還要連結 Javascript 引擎以外的其他程式,例如渲染畫面到銀幕的呈現引擎、HTTP request 獲得資料。雖然 Javascript 可以和其他引擎溝通、改變網頁的模樣、取得資料。但在瀏覽器裡面,呈現引擎、Javascript 引擎、資料請求這些在瀏覽器裡都是非同步執行的

裡面只有 Javascript 引擎自己內部是同步的。

所以當我們非同步的向外請求,或是點擊按鈕執行一個函數時,由於這是非同步處理,瀏覽器的其他部分在執行時,Javascript 也在執行。在執行堆當中,可以看到執行環境被創造,函數被呼叫時,會堆疊在執行堆最上方,結束後離開執行堆。Javascript 引擎內的的等待列稱為事件佇列(event queue) ,內含事件、事件通知等等可能要發生的事情。當在 Javascript 引擎之外的瀏覽器某處有一個需要被通知的事件,在 Javascript 當中就會被放到事件佇列裡,可能需要函數的回應、可以監聽這個事件並且用函數處理。

例如銀幕點擊事件,如果有一個回應 click 事件的函數,或可能有另一個正在執行的程式碼,例如取得資料,必須等執行堆結束空掉後,Javascript 才會進行到事件佇列,如果有,它會看是否有函數會被這個事件觸發。

所以這不是真正的非同步,而是瀏覽器非同步的把事件放到事件佇列,而原本的程式仍然一行一行執行

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

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

// listen for the click event
document.addEventListener("click", clickHandler);

waitThreeSeconds();
console.log("finished execution");

必須在創造執行環境給 clickHandler 之前,依序等函數完成、全域程式碼完成,因為 Javascript 只會依序執行完執行堆後,才會去看事件佇列。所以上面需花 3 秒鐘才完成的函數,長時間函數可以干擾事件。

這就是用同步處理的方式處理非同步事件,根據事件發生的順序處理,因此非同步回呼在 Javascript 是成立的,但非同步的部份是發生在 Javascript 引擎之外。在瀏覽器裡面。

Powered by Hexo and Hexo-theme-hiker

Copyright © 2013 - 2020 CYC'S BLOG All Rights Reserved.

UV : | PV :