2022-08-16|閱讀時間 ‧ 約 8 分鐘

Javascript : Event Queue & Event Loop

前言

這是第一次寫技術文章,但其實應該也只能說是蒐集很多資料並學習如何透過自己的話解釋的內容,並不能像其他大神可能分享一些很酷的技術,目標就單純是為了完成最後一週的作業(如下)。
setTimeout(function(){console.log('delay 0 sec')}, 0)
console.log('Hello!')
透過兩天左右的閱讀資料讓自己對JS在同步與非同步有更多概念,由於有許多專有名詞,因此會先針對各專有名詞作介紹,再敘述自己的看法,最後提供這次參考的網路資源

走入非同步之前

執行環境(Execution Context)
Javascript被執行的地方,在Chrome裡,就是由V8 Engine提供此環境,任何程式碼被執行與讀取的地方都是執行環境,其中又可分為下列三種類型:
  • 全域執行環境 : 預設的執行環境,也就是一開始載入JS檔案的執行環境就是全域執行環境,不在任何函式裡面的程式碼就是在全域執行環境,其功用除了創造全域變數,還會進行記憶體的指派。
  • 函式執行環境 : 就如字面上的意思,即函式被呼叫執行的環境,此執行環境可存在很多個,須注意的是其並不會產生全域物件。
  • eval函式執行環境 : eval對開發者來說是非常方便的函式,可以透過字串方式執行JS,需注意的是如透過EVAL綁定的資料未經過適當編碼與驗證,可能造成注入攻擊的產生,因此現在也較不被推薦使用,甚至在CSP(Content Security Policy,可看成Server回送給Cliet端的Http header裡面的一種安全規範)裡面已經被預設為禁止使用。
執行環境堆疊 (Execution stack)
上面提到函式執行環境可能有很多個,每一個函式被呼叫就會產生一個與之對應的執行環境,此時會透過「堆疊」這種資料結構來儲存函式執行環境,為一種「後進先出」的資料結構。
單線程 (Single-Thread)
一次只能做一件事情,如果有很多事情就會進行排隊,再逐一執行。
非同步(Asynchronous)
即為同一時間可以處理不只一件事情(註: 其他事情亦非同時進行),這在串接第三方API教材的時候就有用到,利用Ajax可以讓瀏覽器向server送出請求時,不需要等待結果仍然可以處裡其他事情,待之後收到response之後,新的內容可以隨時更新至網頁,這也是為什麼瀏覽Facebook、Gmail的時候不需要看到網頁一直重新整理。
Javascript Runtime Environment (JRE)
JRE即為Javascript執行的環境,最常使用的runtiime就是瀏覽器。我們在宣告函數時,使用到的let、const、function、if else,這些都是Javascript的一部分,但其實之前常常使用到的DOM、console.log、這些其實都不是Javascript,而是瀏覽器這個runtime開放給 Javascript 的 API。也就是說不同的runtime會提供不同的東西,例如瀏覽器的DOM就不能再Node.js執行。

研究分享

Javascript是一個同步的程式語言,代表同一時間只做一件事情,也就是一次只跑一段程式碼。但想想如果我們在讀取Facebook網頁,JS除了要渲染畫面還要透過Http請求跟後端拿資料,但如果文章資料很多的時候,在取得所有資料前就都會無法執行其他任務,造成Blocking的現象,因此JS其實也是有非同步執行的能力,讓使用者瀏覽網頁更為順暢。至於如何處理「同步」與「非同步」的動作呢,就要透過我們今天的主角 —Event Queue、 Event Loop。
上面的名詞解釋曾提到DOM其實不算Javascript語言,而是瀏覽器提供的API,讓我們使用者可以在開發時直接使用,其較常使用的API有操作DOM節點的API — document.querySelector、計時的API — setTimeout、 AJAX相關 — XMLHttpRequest。這些由runtime提供的API,不會影響主執行環境的運行,讓網頁可以同時做很多事情(非同步),而這些非同步的行為要如何進行排序呢,即透過Event Queue。Event Queue專門用來執行非同步的函式,等整個主執行環境運行結束以後,才開始依序執行事件儲列裡面的函式,屬於「先進先出」的資料結構
setTimeout(function(){console.log('delay 0 sec')}, 0) 
console.log('Hello!')
因此上述程式碼可看成,Javacript 引擎執行到瀏覽器提供的setTimeout函式時並不會真的停下來,過0秒馬上執行console.log('delay 0 sec'),是在過0秒之後將此函數放到Event Queue,這就像一個待辦清單,裡面的程式不會馬上執行,要所有的全域執行環境與函式執行環境結束後,才會開始執行Event Queue的內容。那要如何知道主環境都已經執行完程式了呢,就是透過Event loop。Event loop可以看成一個無時無刻都在執行的程式,會不斷監聽程式是否全部執行完成,如果環境堆疊是空的,就會去檢查Event Queue是不是有函式待執行,並將Queue的函式移到Stack執行這些「待辦清單」

參考資源

  1. ALPHA Camp教材
  2. [筆記] 理解 JavaScript 中的事件循環、堆疊、佇列和併發模式(Learn event loop, stack, queue, and concurrency mode of JavaScript in depth) ~ PJCHENderbr那些沒告訴你的小細節
  3. 從「為什麼不能用這個函式」談執行環境(runtime) - Huli
  4. JavaScript 原力覺醒 - 成為絕地武士之路 :: 第 11 屆 iThome 鐵人賽
  5. Understanding Event Loop, Call Stack, Event & Job Queue in Javascript | by Rahul Sagore | Medium
  6. 所以說event loop到底是什麼玩意兒?| Philip Roberts | JSConf EU - YouTube
分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.