事件循環EventLoop
短連結系統
前情提要
本篇文章要說解釋的是事件循環(event loop)是什麼????
要說明此東西,要先知到什麼情況下,會遇到、觸發事件循環(event loop)
我們這邊先來舉個例子,今天我想印出一個HI,並且過五秒後,印出There,最後再印一個JS
> code Console(印出順序)
>
> console.log(`hi`) Hi
> setTimeout(function(){ JS
> console.log(`There`); There
> }, 5000);
>
> console.log(`JS`)
承接上面印出的東西,會發現明明理想中的答案是HI > There > JS,為啥實際印出來不太一樣,有人可能會疑問, 大概是setTimeout設定太久的關係,那我們把原本的5s,改成0s
> code Console(印出順序)
>
> console.log(`hi`) Hi
> setTimeout(function(){ JS
> console.log(`There`); There
> }, 5000);
>
> console.log(`JS`)
結果會發現,疑答案還是跟5s的時候一樣欸,到底為啥會這樣呢?
這邊就是我們今天要介紹的,事件循環是什麼?他幫我們做了哪些事?
本篇文章提到的重點會有以些幾點
- Stack是什麼
- 單執行序(one thread)是什麼
- 非同步回調(Async Callback)是什麼
- webAPIs & queue
- 事件循環是什麼
Stack 是什麼
首先我們要來介紹的,就是Stack拉,這個東西在瀏覽器是一個非常重要的東西 如果今天我們呼叫一段JS函式,就會被放到Stack裡面
> JS
> heap stack
> 口 btn()
> 口 name()
> 口 cool()
> 口 OK()
>
> 左半部是記憶體分配 右半部是執行函數的地方
> memory allocation execution context
- heap是記憶體,就是進行記憶體分配的地方
- call stack是一種資料結構,他能夠紀錄幾種情況下,我們在程式中的哪些位置,比如說有一段函式,我們呼叫/使用他,這一段函示會被存放到stack裡面,有很多組函示的話,就會一層一層照執行順序堆疊到stack上,當回傳完函示裡面的東西後,該函式就會從stack裡被移除(依照先進後出的順序)
–
但是!
如果今天你用setTimeout 或是 DOM 或是 HTTP 並不會在這
Ps. 說不在這不太精確,等等會提到setTimeout所在位置
–
單執行序 one thread
再繼續提到下一個東西前,我們要先了解一下單執行序是蝦咪?
單執行緒的也就是程式執行時,所跑的程式路徑(處理的東西)是連續順序下來的,必須前面的處理好,後面的才會執行到。
Ps. JS就是單執行序的程式語言
既然JS是單執行序,就會有一個問題,如果今天在一堆程式碼中,中間有一段要執行很久的程式,那不就全部阻塞在那邊了嗎?
如果今天進入一個網站,他要讀一個檔案讀超級久,在該檔案沒讀完前,整個網站都不能動,那肯定是一個大災難
阻塞 blocking
什麼情況會造成執行速度變慢 => 其實blocking沒有很明確的定義,只要知道會造成執行速度變慢,是因為有一段執行很慢的程式碼
ex. 你執行了一段while很長的迴圈,while迴圈後面還有其他程式,不過那些程式都要等到while迴圈跑完,才會執行,這邊這個有點塞住的狀況,就是blocking
Ps. 那要如何解決blocking呢?JS使用到了 Async Callback 方法,後面會提到
–
one thread == one call back == one thing at a time
單執行序 = 一次只有一個call = 一次只做一件事
–
非同步回調 Async Callback
再來就提到JS怎麼解決這個狀況了,大家在使用瀏覽器的時候應該都有注意到
瀏覽器是不會 blocking 的(要不然使用者應該會發瘋,每動一步都要等一下才能做下一步)
ps. node也不會blocking
實際範例
這邊又可以在把一開始那段程式碼,再拿出來一次
> code Console
>
> console.log(`hi`) Hi
> setTimeout(function(){ JS
> console.log(`There`); There
> }, 5000);
>
> console.log(`JS`)
可以注意到,There雖然被寫在第二段,照剛剛blocking提到的狀況
應該要 印出Hi -> 過5s後,印出There -> 最後印出JS才對,不過為啥這邊反而是There會是最後出現。
原因就是非同步回調的關係,如果今天有一段程式要耗時的時間比較久
瀏覽器會跟這一段程式說:你先繼續執行,不過在你還沒把結果跑出來的這段時間
我先去執行下面的程式,等你回全跑完之後,我再來印出你的結果
Ps. 這邊可能會有人很好奇,那這一段在stack上的出現順序是怎麼樣的,這個因為要在提到另外個東西的關係,後面會提到。
–
JavaScript Runtime 一次只能做一件事,但是瀏覽器會提供我們其他的東西
這些東西是我們無法取得的執行緒,我們只能呼叫他們
–
webAPIs & queue
接下來就要來說明,剛剛的setTimeOut,在瀏覽器上到底是怎麼運作的
首先先記住這一張圖
Ps. webAPI是瀏覽器額外提供的東西
> JS webAPIs
> heap stack
> |口 btn() | | DOM |
> | 口 name() | --> | AJAX(fetch) |
> | 口 cool() | | setTimeOut |
> | 口 OK() | | |
> -------------------------- ----------------
> |
> event loop
> |
> ------------------callback queue------------------------
>
> onClick、onLoad、onDone
> ------------------callback queue------------------------
非同步回調在stack、webAPI、queue、console.log上實際發生的狀況
我們現在再來跑一次剛剛的setTimeout
> code Console
>
> console.log(`hi`) Hi
> setTimeout(function(){ JS
> console.log(`There`); There
> }, 5000);
>
> console.log(`JS`)
> **文字註解**
> 1.當setTimeout被執行時,一開始會在stack,但是由於他要執行一段時間,就會轉移到webAPI
>
> stack webAPIs
>
> | | | |
> | | --> | |
> | | | |
> | setTimeout | | |
> --------------- ----------------
> |
> event loop
> |
> -------callback queue---------
>
>
> -------callback queue---------
> =========================================
> 2.此時stack中其他程式會繼續執行,而setTimeOut一樣在webAPI中繼續跑
> 而當他跑完時,不會直接放到stack(這樣會導致setTimeout隨機出現在程式碼中)
> 而是會跑到另一個地方 queue
>
> stack webAPIs
>
> | | | |
> | | --> | |
> | | | setTimeOut | > 在webAPI持續執行中
> | | | |
> --------------- ----------------
> |
> event loop
> |
> -------callback queue---------
>
>
> -------callback queue---------
> ========================================
> 3.這裡就是任務佇列、回調佇列發揮作用的地方,所有在webAPI跑完的函式,
> 都會自動送到queue中排隊
>
> stack webAPIs
>
> | | | |
> | | --> | |
> | | | |
> | | | |
> --------------- ----------------
> |
> event loop
> |
> -------callback queue---------
> setTimeOut -> 在 queue 中等待執行如果stack是空的,就會
> 把 queue 的第一個任務,丟回
> -------callback queue---------
> ========================================
> 4.EventLoop執行
>
> stack webAPIs
>
> | | | |
> | | --> | |
> | | | |
> | setTimeOut | | |
> --------------- ----------------
> |
> event loop -> 如果stack是空的,就會把 queue 的第一個任務,丟回stack
> |
> -------callback queue---------
>
> -------callback queue---------
看完上面整個圖解流程後,應該可以了解seTimeOut在瀏覽器執行的流程,有了這個流程,很好了解決JS單執行序的問題
並且還多了一些很有趣的應用(ex. GoogleMap的實際應用)
事件循環到底是什麼
最後,我們回來一開始的問題,事件循環到底是什麼呢??
他就像是上面這整個方程式中,最單純的小拼圖,他只有一個非常簡單的小工作
他的任務是查看 stack、queue,如果 stack 是空的,他就會將queue的佇列中,第一個東西放回 stack上,讓堆疊能執行
–
如果今天有其他的AJAX請求,都會以wevAPI這套相同方式去運作
ex. 今天我想要用爬蟲去爬取別人的資料、讀取別給我的的JSON檔案
這些程式執行時都會先轉移到webAPI,等到stack清空時再執行
–