JavaScript 是一個直譯式語言,而直譯語言無法獨立執行,必須仰賴一個能夠編譯及執行的環境(即JavaScript 引擎),最有名的 JavaScript 引擎就是 Google 所開發的 V8 引擎,被用在 Chrome 瀏覽器還有 Node.js 上,所以我們可以說瀏覽器及Node.js是JavaScript的執行環境。
而剛剛提到的JavaScript 引擎,是使用單執行緒 (Single thread) 來執行 JavaScript 的,也就是一次只能做一件事,若有很多件事要做,就會讓這些事情排隊,並逐一執行。所以在執行 JavaScript 的時候,是屬於同步執行的(一步一步執行,這步還沒執行完的話不會到下一步執行)。
光從字面上來看"同步"與"非同步"會容易誤會他們的意思,同步聽起來很像同時做很多件事,但其實是相反的,同步
在這裡的含意是一次只做一件事情,直到這件事做完才會繼續做下一件事
。
可以把它想成是在接收訊息時,會立刻(或盡可能接近立刻)執行的即時通訊(如電話:在電話通訊時,你通常會立刻回覆對方的訊息)。
由於同步的特性是一件事做完才接著做下一件事,所以當 JavaScript 在瀏覽器同步執行耗時的函數時,除了會阻塞 (block) 後面函數的執行外,還會阻塞整個網頁的畫面,讓整個網頁的畫面動彈不得。
非同步的概念則是相反,是同時可以做很多件事情,不需要等到前一件事情做完才做下一件事情
。
也可以把非同步想成是兩方或多方之間交換消息的方法,其中每一方在方便或可能的時候才接收並處理訊息,而不是立刻這麼做的通訊環境。(如電子郵件:收件者會在方便時才回覆,他們不用馬上回覆)
前面提到JavaScript是單執行緒,那為什麼還可以處理非同步事件呢?
這是因為瀏覽器提供了很多WebAPI如setTimeout、DOM、AJAX、HTTP request,這些不存在V8引擎中,是瀏覽器提供的資源。
JavaScript 在執行的時候是依照同步的概念去執行的,當 JavaScript 引擎中的 Call Stack 發現執行到非同步函數時,會將該非同步函數的觸發事件交給瀏覽器處理,當該非同步函數的執行條件被觸發時時,再將非同步函數的 callback function 放到一個叫做 Callback Queue 的資料結構中。
瀏覽器中的 event loop 則會不斷監聽 Call Stack 中是否有還沒執行完的函數,一旦 Call Stack 中為空,event loop 就會將 Callback Queue 裡的第一個 callback function 放到 Call Stack 中執行。
所以在瀏覽器內,只有 Javascript 引擎本身是同步的,而 Javascript 引擎可以跟 WebAPI 溝通,達到非同步的事件處理。
參考來源:
https://www.oxxostudio.tw/articles/201706/javascript-promise-settimeout.html
https://jimmyswebnote.com/javascript-sync-async/